/*
   Copyright (c) 2000, 2011, 2012 Oracle and/or its affiliates. All rights reserved.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
*/


/**
  @file

  @brief
  This file implements classes defined in field.h
*/

#include "sql_priv.h"
#include "sql_select.h"
#include "strfunc.h"                            // find_type2, find_set
#include "sql_time.h"                    // str_to_datetime_with_warn,
#include "tztime.h"                      // struct Time_zone
#include "filesort.h"                    // change_double_for_sort
#include <m_ctype.h>
#include <errno.h>
#include "sql_join_buffer.h"             // CACHE_FIELD

using std::max;
using std::min;

// Maximum allowed exponent value for converting string to decimal
#define MAX_EXPONENT 1024

/**
  Static variables
*/
uchar Field_null::null[1] = {1};
const char field_separator = ',';

#define DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE FLOATING_POINT_BUFFER
#define LONGLONG_TO_STRING_CONVERSION_BUFFER_SIZE 128
#define DECIMAL_TO_STRING_CONVERSION_BUFFER_SIZE 128
#define BLOB_PACK_LENGTH_TO_MAX_LENGH(arg) \
    ((ulong) ((LL(1) << MY_MIN(arg, 4) * 8) - LL(1)))

/*
  Rules for merging different types of fields in UNION

  NOTE: to avoid 256*256 table, gap in table types numeration is skiped
  following #defines describe that gap and how to canculate number of fields
  and index of field in thia array.
*/
#define FIELDTYPE_TEAR_FROM (MYSQL_TYPE_BIT + 1)
#define FIELDTYPE_TEAR_TO   (MYSQL_TYPE_NEWDECIMAL - 1)
#define FIELDTYPE_NUM (FIELDTYPE_TEAR_FROM + (255 - FIELDTYPE_TEAR_TO))


inline int field_type2index (enum_field_types field_type)
{
    field_type = real_type_to_type(field_type);
    DBUG_ASSERT(field_type < FIELDTYPE_TEAR_FROM ||
                field_type > FIELDTYPE_TEAR_TO);
    return (field_type < FIELDTYPE_TEAR_FROM ?
            field_type :
            ((int)FIELDTYPE_TEAR_FROM) + (field_type - FIELDTYPE_TEAR_TO) - 1);
}


static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM] = {
    /* MYSQL_TYPE_DECIMAL -> */
    {
        //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
        MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_NEWDECIMAL,
        //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
        MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_NEWDECIMAL,
        //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
        MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_DOUBLE,
        //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
        MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
        MYSQL_TYPE_DECIMAL,     MYSQL_TYPE_DECIMAL,
        //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_BIT          <16>-<245>
        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
        MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_STRING       MYSQL_TYPE_GEOMETRY
        MYSQL_TYPE_STRING,      MYSQL_TYPE_VARCHAR
    },
    /* MYSQL_TYPE_TINY -> */
    {
        //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
        MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_TINY,
        //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
        MYSQL_TYPE_SHORT,       MYSQL_TYPE_LONG,
        //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
        MYSQL_TYPE_FLOAT,       MYSQL_TYPE_DOUBLE,
        //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
        MYSQL_TYPE_TINY,        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
        MYSQL_TYPE_LONGLONG,    MYSQL_TYPE_INT24,
        //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY,
        //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_BIT          <16>-<245>
        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
        MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_STRING       MYSQL_TYPE_GEOMETRY
        MYSQL_TYPE_STRING,      MYSQL_TYPE_VARCHAR
    },
    /* MYSQL_TYPE_SHORT -> */
    {
        //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
        MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_SHORT,
        //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
        MYSQL_TYPE_SHORT,       MYSQL_TYPE_LONG,
        //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
        MYSQL_TYPE_FLOAT,       MYSQL_TYPE_DOUBLE,
        //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
        MYSQL_TYPE_SHORT,       MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
        MYSQL_TYPE_LONGLONG,    MYSQL_TYPE_INT24,
        //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_SHORT,
        //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_BIT          <16>-<245>
        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
        MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_STRING       MYSQL_TYPE_GEOMETRY
        MYSQL_TYPE_STRING,      MYSQL_TYPE_VARCHAR
    },
    /* MYSQL_TYPE_LONG -> */
    {
        //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
        MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_LONG,
        //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
        MYSQL_TYPE_LONG,        MYSQL_TYPE_LONG,
        //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
        MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_DOUBLE,
        //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
        MYSQL_TYPE_LONG,         MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
        MYSQL_TYPE_LONGLONG,    MYSQL_TYPE_INT24,
        //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_LONG,
        //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_BIT          <16>-<245>
        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
        MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_STRING       MYSQL_TYPE_GEOMETRY
        MYSQL_TYPE_STRING,      MYSQL_TYPE_VARCHAR
    },
    /* MYSQL_TYPE_FLOAT -> */
    {
        //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
        MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_FLOAT,
        //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
        MYSQL_TYPE_FLOAT,       MYSQL_TYPE_DOUBLE,
        //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
        MYSQL_TYPE_FLOAT,       MYSQL_TYPE_DOUBLE,
        //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
        MYSQL_TYPE_FLOAT,       MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
        MYSQL_TYPE_FLOAT,       MYSQL_TYPE_INT24,
        //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_FLOAT,
        //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_BIT          <16>-<245>
        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
        MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_STRING       MYSQL_TYPE_GEOMETRY
        MYSQL_TYPE_STRING,      MYSQL_TYPE_VARCHAR
    },
    /* MYSQL_TYPE_DOUBLE -> */
    {
        //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
        MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_DOUBLE,
        //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
        MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_DOUBLE,
        //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
        MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_DOUBLE,
        //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
        MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
        MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_INT24,
        //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_DOUBLE,
        //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_BIT          <16>-<245>
        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
        MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_STRING       MYSQL_TYPE_GEOMETRY
        MYSQL_TYPE_STRING,      MYSQL_TYPE_VARCHAR
    },
    /* MYSQL_TYPE_NULL -> */
    {
        //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
        MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_TINY,
        //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
        MYSQL_TYPE_SHORT,       MYSQL_TYPE_LONG,
        //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
        MYSQL_TYPE_FLOAT,       MYSQL_TYPE_DOUBLE,
        //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
        MYSQL_TYPE_NULL,        MYSQL_TYPE_TIMESTAMP,
        //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
        MYSQL_TYPE_LONGLONG,    MYSQL_TYPE_INT24,
        //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
        MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_TIME,
        //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
        MYSQL_TYPE_DATETIME,    MYSQL_TYPE_YEAR,
        //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
        MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_BIT          <16>-<245>
        MYSQL_TYPE_BIT,
        //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
        MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_ENUM,
        //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
        MYSQL_TYPE_SET,         MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_STRING       MYSQL_TYPE_GEOMETRY
        MYSQL_TYPE_STRING,      MYSQL_TYPE_GEOMETRY
    },
    /* MYSQL_TYPE_TIMESTAMP -> */
    {
        //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
        MYSQL_TYPE_TIMESTAMP,   MYSQL_TYPE_TIMESTAMP,
        //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
        MYSQL_TYPE_DATETIME,    MYSQL_TYPE_DATETIME,
        //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
        MYSQL_TYPE_DATETIME,    MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
        MYSQL_TYPE_DATETIME,    MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_BIT          <16>-<245>
        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_STRING       MYSQL_TYPE_GEOMETRY
        MYSQL_TYPE_STRING,      MYSQL_TYPE_VARCHAR
    },
    /* MYSQL_TYPE_LONGLONG -> */
    {
        //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
        MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_LONGLONG,
        //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
        MYSQL_TYPE_LONGLONG,    MYSQL_TYPE_LONGLONG,
        //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
        MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_DOUBLE,
        //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
        MYSQL_TYPE_LONGLONG,    MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
        MYSQL_TYPE_LONGLONG,    MYSQL_TYPE_LONG,
        //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_LONGLONG,
        //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
        MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_BIT          <16>-<245>
        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
        MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_STRING       MYSQL_TYPE_GEOMETRY
        MYSQL_TYPE_STRING,      MYSQL_TYPE_VARCHAR
    },
    /* MYSQL_TYPE_INT24 -> */
    {
        //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
        MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_INT24,
        //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
        MYSQL_TYPE_INT24,       MYSQL_TYPE_LONG,
        //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
        MYSQL_TYPE_FLOAT,       MYSQL_TYPE_DOUBLE,
        //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
        MYSQL_TYPE_INT24,       MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
        MYSQL_TYPE_LONGLONG,    MYSQL_TYPE_INT24,
        //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_INT24,
        //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
        MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_BIT          <16>-<245>
        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NEWDECIMAL    MYSQL_TYPE_ENUM
        MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_STRING       MYSQL_TYPE_GEOMETRY
        MYSQL_TYPE_STRING,      MYSQL_TYPE_VARCHAR
    },
    /* MYSQL_TYPE_DATE -> */
    {
        //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
        MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_DATETIME,
        //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
        MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_DATETIME,
        //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
        MYSQL_TYPE_DATETIME,    MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
        MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_BIT          <16>-<245>
        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
        MYSQL_TYPE_VARCHAR,  MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_STRING       MYSQL_TYPE_GEOMETRY
        MYSQL_TYPE_STRING,      MYSQL_TYPE_VARCHAR
    },
    /* MYSQL_TYPE_TIME -> */
    {
        //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
        MYSQL_TYPE_TIME,        MYSQL_TYPE_DATETIME,
        //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
        MYSQL_TYPE_DATETIME,    MYSQL_TYPE_TIME,
        //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
        MYSQL_TYPE_DATETIME,    MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
        MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_BIT          <16>-<245>
        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_STRING       MYSQL_TYPE_GEOMETRY
        MYSQL_TYPE_STRING,      MYSQL_TYPE_VARCHAR
    },
    /* MYSQL_TYPE_DATETIME -> */
    {
        //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
        MYSQL_TYPE_DATETIME,    MYSQL_TYPE_DATETIME,
        //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
        MYSQL_TYPE_DATETIME,    MYSQL_TYPE_DATETIME,
        //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
        MYSQL_TYPE_DATETIME,    MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
        MYSQL_TYPE_DATETIME,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_BIT          <16>-<245>
        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_STRING       MYSQL_TYPE_GEOMETRY
        MYSQL_TYPE_STRING,      MYSQL_TYPE_VARCHAR
    },
    /* MYSQL_TYPE_YEAR -> */
    {
        //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
        MYSQL_TYPE_DECIMAL,     MYSQL_TYPE_TINY,
        //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
        MYSQL_TYPE_SHORT,       MYSQL_TYPE_LONG,
        //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
        MYSQL_TYPE_FLOAT,       MYSQL_TYPE_DOUBLE,
        //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
        MYSQL_TYPE_YEAR,        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
        MYSQL_TYPE_LONGLONG,    MYSQL_TYPE_INT24,
        //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_YEAR,
        //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_BIT          <16>-<245>
        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
        MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_STRING       MYSQL_TYPE_GEOMETRY
        MYSQL_TYPE_STRING,      MYSQL_TYPE_VARCHAR
    },
    /* MYSQL_TYPE_NEWDATE -> */
    {
        //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
        MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_DATETIME,
        //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
        MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_DATETIME,
        //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
        MYSQL_TYPE_DATETIME,    MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
        MYSQL_TYPE_NEWDATE,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_BIT          <16>-<245>
        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_STRING       MYSQL_TYPE_GEOMETRY
        MYSQL_TYPE_STRING,      MYSQL_TYPE_VARCHAR
    },
    /* MYSQL_TYPE_VARCHAR -> */
    {
        //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_BIT          <16>-<245>
        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_STRING       MYSQL_TYPE_GEOMETRY
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR
    },
    /* MYSQL_TYPE_BIT -> */
    {
        //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
        MYSQL_TYPE_BIT,         MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_BIT          <16>-<245>
        MYSQL_TYPE_BIT,
        //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_STRING       MYSQL_TYPE_GEOMETRY
        MYSQL_TYPE_STRING,      MYSQL_TYPE_VARCHAR
    },
    /* MYSQL_TYPE_NEWDECIMAL -> */
    {
        //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
        MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_NEWDECIMAL,
        //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
        MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_NEWDECIMAL,
        //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
        MYSQL_TYPE_DOUBLE,      MYSQL_TYPE_DOUBLE,
        //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
        MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
        MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_NEWDECIMAL,
        //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_NEWDECIMAL,
        //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_BIT          <16>-<245>
        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
        MYSQL_TYPE_NEWDECIMAL,  MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_STRING       MYSQL_TYPE_GEOMETRY
        MYSQL_TYPE_STRING,      MYSQL_TYPE_VARCHAR
    },
    /* MYSQL_TYPE_ENUM -> */
    {
        //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
        MYSQL_TYPE_ENUM,        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_BIT          <16>-<245>
        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_STRING       MYSQL_TYPE_GEOMETRY
        MYSQL_TYPE_STRING,      MYSQL_TYPE_VARCHAR
    },
    /* MYSQL_TYPE_SET -> */
    {
        //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
        MYSQL_TYPE_SET,         MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_BIT          <16>-<245>
        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_STRING       MYSQL_TYPE_GEOMETRY
        MYSQL_TYPE_STRING,      MYSQL_TYPE_VARCHAR
    },
    /* MYSQL_TYPE_TINY_BLOB -> */
    {
        //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
        MYSQL_TYPE_TINY_BLOB,   MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
        MYSQL_TYPE_TINY_BLOB,   MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
        MYSQL_TYPE_TINY_BLOB,   MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
        MYSQL_TYPE_TINY_BLOB,   MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
        MYSQL_TYPE_TINY_BLOB,   MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
        MYSQL_TYPE_TINY_BLOB,   MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
        MYSQL_TYPE_TINY_BLOB,   MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
        MYSQL_TYPE_TINY_BLOB,   MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_BIT          <16>-<245>
        MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
        MYSQL_TYPE_TINY_BLOB,   MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
        MYSQL_TYPE_TINY_BLOB,   MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_STRING       MYSQL_TYPE_GEOMETRY
        MYSQL_TYPE_TINY_BLOB,   MYSQL_TYPE_TINY_BLOB
    },
    /* MYSQL_TYPE_MEDIUM_BLOB -> */
    {
        //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
        //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
        //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
        //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
        //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
        //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
        //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
        //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
        //MYSQL_TYPE_BIT          <16>-<245>
        MYSQL_TYPE_MEDIUM_BLOB,
        //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
        //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
        //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB,
        //MYSQL_TYPE_STRING       MYSQL_TYPE_GEOMETRY
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB
    },
    /* MYSQL_TYPE_LONG_BLOB -> */
    {
        //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
        MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
        MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
        MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
        MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
        MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
        MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
        MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
        MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_BIT          <16>-<245>
        MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
        MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
        MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
        MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
        MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_STRING       MYSQL_TYPE_GEOMETRY
        MYSQL_TYPE_LONG_BLOB,   MYSQL_TYPE_LONG_BLOB
    },
    /* MYSQL_TYPE_BLOB -> */
    {
        //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_BLOB,
        //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_BLOB,
        //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_BLOB,
        //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_BLOB,
        //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_BLOB,
        //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_BLOB,
        //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_BLOB,
        //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_BLOB,
        //MYSQL_TYPE_BIT          <16>-<245>
        MYSQL_TYPE_BLOB,
        //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_BLOB,
        //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_BLOB,
        //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_BLOB,
        //MYSQL_TYPE_STRING       MYSQL_TYPE_GEOMETRY
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_BLOB
    },
    /* MYSQL_TYPE_VAR_STRING -> */
    {
        //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_BIT          <16>-<245>
        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_STRING       MYSQL_TYPE_GEOMETRY
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR
    },
    /* MYSQL_TYPE_STRING -> */
    {
        //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
        MYSQL_TYPE_STRING,      MYSQL_TYPE_STRING,
        //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
        MYSQL_TYPE_STRING,      MYSQL_TYPE_STRING,
        //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
        MYSQL_TYPE_STRING,      MYSQL_TYPE_STRING,
        //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
        MYSQL_TYPE_STRING,      MYSQL_TYPE_STRING,
        //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
        MYSQL_TYPE_STRING,      MYSQL_TYPE_STRING,
        //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
        MYSQL_TYPE_STRING,      MYSQL_TYPE_STRING,
        //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
        MYSQL_TYPE_STRING,      MYSQL_TYPE_STRING,
        //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
        MYSQL_TYPE_STRING,      MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_BIT          <16>-<245>
        MYSQL_TYPE_STRING,
        //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
        MYSQL_TYPE_STRING,      MYSQL_TYPE_STRING,
        //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
        MYSQL_TYPE_STRING,      MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_STRING       MYSQL_TYPE_GEOMETRY
        MYSQL_TYPE_STRING,      MYSQL_TYPE_STRING
    },
    /* MYSQL_TYPE_GEOMETRY -> */
    {
        //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
        MYSQL_TYPE_GEOMETRY,    MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_BIT          <16>-<245>
        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
        MYSQL_TYPE_VARCHAR,     MYSQL_TYPE_TINY_BLOB,
        //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
        MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB,
        //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
        MYSQL_TYPE_BLOB,        MYSQL_TYPE_VARCHAR,
        //MYSQL_TYPE_STRING       MYSQL_TYPE_GEOMETRY
        MYSQL_TYPE_STRING,      MYSQL_TYPE_GEOMETRY
    }
};


/**
  Return type of which can carry value of both given types in UNION result.

  @param a  type for merging
  @param b  type for merging

  @return
    type of field
*/

enum_field_types Field::field_type_merge(enum_field_types a,
        enum_field_types b)
{
    return field_types_merge_rules[field_type2index(a)]
           [field_type2index(b)];
}


static Item_result field_types_result_type [FIELDTYPE_NUM] = {
    //MYSQL_TYPE_DECIMAL      MYSQL_TYPE_TINY
    DECIMAL_RESULT,           INT_RESULT,
    //MYSQL_TYPE_SHORT        MYSQL_TYPE_LONG
    INT_RESULT,               INT_RESULT,
    //MYSQL_TYPE_FLOAT        MYSQL_TYPE_DOUBLE
    REAL_RESULT,              REAL_RESULT,
    //MYSQL_TYPE_NULL         MYSQL_TYPE_TIMESTAMP
    STRING_RESULT,            STRING_RESULT,
    //MYSQL_TYPE_LONGLONG     MYSQL_TYPE_INT24
    INT_RESULT,               INT_RESULT,
    //MYSQL_TYPE_DATE         MYSQL_TYPE_TIME
    STRING_RESULT,            STRING_RESULT,
    //MYSQL_TYPE_DATETIME     MYSQL_TYPE_YEAR
    STRING_RESULT,            INT_RESULT,
    //MYSQL_TYPE_NEWDATE      MYSQL_TYPE_VARCHAR
    STRING_RESULT,            STRING_RESULT,
    //MYSQL_TYPE_BIT          <16>-<245>
    STRING_RESULT,
    //MYSQL_TYPE_NEWDECIMAL   MYSQL_TYPE_ENUM
    DECIMAL_RESULT,           STRING_RESULT,
    //MYSQL_TYPE_SET          MYSQL_TYPE_TINY_BLOB
    STRING_RESULT,            STRING_RESULT,
    //MYSQL_TYPE_MEDIUM_BLOB  MYSQL_TYPE_LONG_BLOB
    STRING_RESULT,            STRING_RESULT,
    //MYSQL_TYPE_BLOB         MYSQL_TYPE_VAR_STRING
    STRING_RESULT,            STRING_RESULT,
    //MYSQL_TYPE_STRING       MYSQL_TYPE_GEOMETRY
    STRING_RESULT,            STRING_RESULT
};


/*
  Test if the given string contains important data:
  not spaces for character string,
  or any data for binary string.

  SYNOPSIS
    test_if_important_data()
    cs          Character set
    str         String to test
    strend      String end

  RETURN
    FALSE - If string does not have important data
    TRUE  - If string has some important data
*/

static bool test_if_important_data(const CHARSET_INFO *cs, const char *str,
                                   const char *strend)
{
    if (cs != &my_charset_bin)
        str += cs->cset->scan(cs, str, strend, MY_SEQ_SPACES);

    return (str < strend);
}


/**
   Function to compare two unsigned integers for their relative order.
   Used below. In an anonymous namespace to not clash with definitions
   in other files.
 */

CPP_UNNAMED_NS_START

int compare(unsigned int a, unsigned int b)
{
    if (a < b)
        return -1;

    if (b < a)
        return 1;

    return 0;
}

CPP_UNNAMED_NS_END

/**
  Detect Item_result by given field type of UNION merge result.

  @param field_type  given field type

  @return
    Item_result (type of internal MySQL expression result)
*/

Item_result Field::result_merge_type(enum_field_types field_type)
{
    return field_types_result_type[field_type2index(field_type)];
}

/*****************************************************************************
  Static help functions
*****************************************************************************/

/**
  Output a warning for erroneous conversion of strings to numerical
  values. For use with ER_TRUNCATED_WRONG_VALUE[_FOR_FIELD]

  @param thd         THD object
  @param str         pointer to string that failed to be converted
  @param length      length of string
  @param cs          charset for string
  @param typestr     string describing type converted to
  @param error       error value to output
  @param field_name  (for *_FOR_FIELD) name of field
  @param row_num     (for *_FOR_FIELD) row number
 */
static void push_numerical_conversion_warning(THD *thd, const char *str,
        uint length,
        const CHARSET_INFO *cs,
        const char *typestr, int error,
        const char *field_name = "UNKNOWN",
        ulong row_num = 0)
{
    char buf[MY_MAX(MY_MAX(DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE,
                           LONGLONG_TO_STRING_CONVERSION_BUFFER_SIZE),
                    DECIMAL_TO_STRING_CONVERSION_BUFFER_SIZE)];
    String tmp(buf, sizeof(buf), cs);
    tmp.copy(str, length, cs);
    push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
                        error, ER(error), typestr, tmp.c_ptr(),
                        field_name, row_num);
}


/**
  Emits a warning for the decimal conversion error. May modify
  dec_value if there was conversion overflow or bad number.


  @param dec_error         decimal library return code
                           (E_DEC_* see include/decimal.h)
  @param dec_value[in,out] Decimal value returned by convertion function.
  @param from              Value converted from
  @param length            Length of 'from'
  @param charset_arg       Charset of 'from'
*/
static void set_decimal_warning(Field_new_decimal *field,
                                int dec_error,
                                my_decimal *dec_value,
                                const char *from,
                                uint length,
                                const CHARSET_INFO *charset_arg)
{
    switch (dec_error) {
    case E_DEC_TRUNCATED:
        field->set_warning(Sql_condition::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED, 1);
        break;

    case E_DEC_OVERFLOW:
        field->set_warning(Sql_condition::WARN_LEVEL_WARN,
                           ER_WARN_DATA_OUT_OF_RANGE, 1);
        field->set_value_on_overflow(dec_value, dec_value->sign());
        break;

    case E_DEC_BAD_NUM:
        ErrConvString errmsg(from, length, charset_arg);
        const Diagnostics_area *da = field->table->in_use->get_stmt_da();
        push_warning_printf(field->table->in_use, Sql_condition::WARN_LEVEL_WARN,
                            ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
                            ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
                            "decimal", errmsg.ptr(), field->field_name,
                            static_cast<ulong>(da->current_row_for_warning()));
        my_decimal_set_zero(dec_value);
    }
}

/**
  Check whether a field type can be partially indexed by a key.

  This is a static method, rather than a virtual function, because we need
  to check the type of a non-Field in mysql_alter_table().

  @param type  field type

  @retval
    TRUE  Type can have a prefixed key
  @retval
    FALSE Type can not have a prefixed key
*/

bool Field::type_can_have_key_part(enum enum_field_types type)
{
    switch (type) {
    case MYSQL_TYPE_VARCHAR:
    case MYSQL_TYPE_TINY_BLOB:
    case MYSQL_TYPE_MEDIUM_BLOB:
    case MYSQL_TYPE_LONG_BLOB:
    case MYSQL_TYPE_BLOB:
    case MYSQL_TYPE_VAR_STRING:
    case MYSQL_TYPE_STRING:
    case MYSQL_TYPE_GEOMETRY:
        return TRUE;

    default:
        return FALSE;
    }
}


/**
  Numeric fields base class constructor.
*/
Field_num::Field_num(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
                     uchar null_bit_arg, utype unireg_check_arg,
                     const char *field_name_arg,
                     uint8 dec_arg, bool zero_arg, bool unsigned_arg)
    : Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
            unireg_check_arg, field_name_arg),
      dec(dec_arg), zerofill(zero_arg), unsigned_flag(unsigned_arg)
{
    if (zerofill)
        flags |= ZEROFILL_FLAG;

    if (unsigned_flag)
        flags |= UNSIGNED_FLAG;
}


void Field_num::prepend_zeros(String *value)
{
    int diff;

    if ((diff = (int) (field_length - value->length())) > 0) {
        bmove_upp((uchar *) value->ptr() + field_length,
                  (uchar *) value->ptr() + value->length(),
                  value->length());
        memset(const_cast<char *>(value->ptr()), '0', diff);
        value->length(field_length);
        (void) value->c_ptr_quick();		// Avoid warnings in purify
    }
}

/**
  Test if given number is a int.

  @todo
    Make this multi-byte-character safe

  @param str		String to test
  @param length        Length of 'str'
  @param int_end	Pointer to char after last used digit
  @param cs		Character set

  @note
    This is called after one has called strntoull10rnd() function.

  @return TYPE_OK, TYPE_ERR_BAD_VALUE or TYPE_WARN_TRUNCATED
*/

type_conversion_status Field_num::check_int(const CHARSET_INFO *cs, const char *str, int length,
        const char *int_end, int error)
{
    /* Test if we get an empty string or wrong integer */
    if (str == int_end || error == MY_ERRNO_EDOM) {
        ErrConvString err(str, length, cs);
        push_warning_printf(table->in_use, Sql_condition::WARN_LEVEL_WARN,
                            ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
                            ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
                            "integer", err.ptr(), field_name,
                            (ulong) table->in_use->get_stmt_da()->
                            current_row_for_warning());
        return TYPE_ERR_BAD_VALUE;
    }

    /* Test if we have garbage at the end of the given string. */
    if (test_if_important_data(cs, int_end, str + length)) {
        set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
        return TYPE_WARN_TRUNCATED;
    }

    return TYPE_OK;
}


/*
  Conver a string to an integer then check bounds.

  SYNOPSIS
    Field_num::get_int
    cs            Character set
    from          String to convert
    len           Length of the string
    rnd           OUT longlong value
    unsigned_max  max unsigned value
    signed_min    min signed value
    signed_max    max signed value

  DESCRIPTION
    The function calls strntoull10rnd() to get an integer value then
    check bounds and errors returned. In case of any error a warning
    is raised.

  @return TYPE_OK, TYPE_WARN_OUT_OF_RANGE, TYPE_ERR_BAD_VALUE or
          TYPE_WARN_TRUNCATED
*/

type_conversion_status Field_num::get_int(const CHARSET_INFO *cs, const char *from, uint len,
        longlong *rnd, ulonglong unsigned_max,
        longlong signed_min, longlong signed_max)
{
    char *end;
    int error;
    *rnd = (longlong) cs->cset->strntoull10rnd(cs, from, len,
            unsigned_flag, &end,
            &error);

    if (unsigned_flag) {
        if ((((ulonglong) *rnd > unsigned_max) &&
                (*rnd = (longlong) unsigned_max)) ||
                error == MY_ERRNO_ERANGE)
            goto out_of_range;

    } else {
        if (*rnd < signed_min) {
            *rnd = signed_min;
            goto out_of_range;

        } else if (*rnd > signed_max) {
            *rnd = signed_max;
            goto out_of_range;
        }
    }

    if (table->in_use->count_cuted_fields != 0)
        return check_int(cs, from, len, end, error);

    return TYPE_OK;
out_of_range:
    set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
    return TYPE_WARN_OUT_OF_RANGE;
}


/*
  This is a generic method which is executed only for
  Field_short, Field_medium, Field_long, Field_longlong and Field_tiny.

  The other field types that come from Field_num override this method:
  Field_real (common parent for Field_decimal, Field_float, Field_double),
  Field_new_decimal, Field_year.
*/
type_conversion_status Field_num::store_time(MYSQL_TIME *ltime,
        uint8 dec_arg __attribute__((unused)))
{
    longlong nr = TIME_to_ulonglong_round(ltime);
    return store(ltime->neg ? -nr : nr, 0);
}


/**
  Process decimal library return codes and issue warnings for overflow and
  truncation.

  @param op_result  decimal library return code (E_DEC_* see include/decimal.h)

  @retval 0 No error or some other errors except overflow
  @retval 1 There was overflow
*/

bool Field::warn_if_overflow(int op_result)
{
    if (op_result == E_DEC_OVERFLOW) {
        set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
        return true;
    }

    if (op_result == E_DEC_TRUNCATED) {
        set_warning(Sql_condition::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED, 1);
        /* We return 0 here as this is not a critical issue */
    }

    return false;
}


/**
  Interpret field value as an integer but return the result as a string.

  This is used for printing bit_fields as numbers while debugging.
*/

String *Field::val_int_as_str(String *val_buffer, my_bool unsigned_val)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    const CHARSET_INFO *cs = &my_charset_bin;
    uint length;
    longlong value = val_int();

    if (val_buffer->alloc(MY_INT64_NUM_DECIMAL_DIGITS))
        return 0;

    length = (uint) (*cs->cset->longlong10_to_str)(cs, (char *) val_buffer->ptr(),
             MY_INT64_NUM_DECIMAL_DIGITS,
             unsigned_val ? 10 : -10,
             value);
    val_buffer->length(length);
    return val_buffer;
}


/// This is used as a table name when the table structure is not set up
Field::Field(uchar *ptr_arg, uint32 length_arg, uchar *null_ptr_arg,
             uchar null_bit_arg,
             utype unireg_check_arg, const char *field_name_arg)
    : ptr(ptr_arg), null_ptr(null_ptr_arg),
      table(0), orig_table(0), table_name(0),
      field_name(field_name_arg),
      unireg_check(unireg_check_arg),
      field_length(length_arg), null_bit(null_bit_arg),
      is_created_from_null_item(FALSE)
{
    flags = null_ptr ? 0 : NOT_NULL_FLAG;
    comment.str = (char *) "";
    comment.length = 0;
    field_index = 0;
}


void Field::hash(ulong *nr, ulong *nr2)
{
    if (is_null())
        *nr ^= (*nr << 1) | 1;

    else {
        uint len = pack_length();
        const CHARSET_INFO *cs = sort_charset();
        cs->coll->hash_sort(cs, ptr, len, nr, nr2);
    }
}

size_t Field::do_last_null_byte() const
{
    DBUG_ASSERT(null_ptr == NULL || null_ptr >= table->record[0]);

    if (null_ptr)
        return null_offset() + 1;

    return LAST_NULL_BYTE_UNDEF;
}


void Field::copy_from_tmp(int row_offset)
{
    memcpy(ptr, ptr + row_offset, pack_length());

    if (null_ptr) {
        *null_ptr = (uchar) ((null_ptr[0] & (uchar) ~(uint) null_bit) |
                             (null_ptr[row_offset] & (uchar) null_bit));
    }
}


bool Field::send_binary(Protocol *protocol)
{
    char buff[MAX_FIELD_WIDTH];
    String tmp(buff, sizeof(buff), charset());
    val_str(&tmp);
    return protocol->store(tmp.ptr(), tmp.length(), tmp.charset());
}


/**
   Check to see if field size is compatible with destination.

   This method is used in row-based replication to verify that the
   slave's field size is less than or equal to the master's field
   size. The encoded field metadata (from the master or source) is
   decoded and compared to the size of this field (the slave or
   destination).

   @note

   The comparison is made so that if the source data (from the master)
   is less than the target data (on the slave), -1 is returned in @c
   <code>*order_var</code>. This implies that a conversion is
   necessary, but that it is lossy and can result in truncation of the
   value.

   If the source data is strictly greater than the target data, 1 is
   returned in <code>*order_var</code>. This implies that the source
   type can is contained in the target type and that a conversion is
   necessary but is non-lossy.

   If no conversion is required to fit the source type in the target
   type, 0 is returned in <code>*order_var</code>.

   @param   field_metadata   Encoded size in field metadata
   @param   mflags           Flags from the table map event for the table.
   @param   order_var        Pointer to variable where the order
                             between the source field and this field
                             will be returned.

   @return @c true if this field's size is compatible with the
   master's field size, @c false otherwise.
*/
bool Field::compatible_field_size(uint field_metadata,
                                  Relay_log_info *rli_arg __attribute__((unused)),
                                  uint16 mflags __attribute__((unused)),
                                  int *order_var)
{
    uint const source_size = pack_length_from_metadata(field_metadata);
    uint const destination_size = row_pack_length();
    DBUG_PRINT("debug", ("real_type: %d, source_size: %u, destination_size: %u",
                         real_type(), source_size, destination_size));
    *order_var = compare(source_size, destination_size);
    return true;
}


type_conversion_status Field::store(const char *to, uint length, const CHARSET_INFO *cs,
                                    enum_check_fields check_level)
{
    enum_check_fields old_check_level = table->in_use->count_cuted_fields;
    table->in_use->count_cuted_fields = check_level;
    const type_conversion_status res = store(to, length, cs);
    table->in_use->count_cuted_fields = old_check_level;
    return res;
}


/**
   Pack the field into a format suitable for storage and transfer.

   To implement packing functionality, only the virtual function
   should be overridden. The other functions are just convenience
   functions and hence should not be overridden.

   The value of <code>low_byte_first</code> is dependent on how the
   packed data is going to be used: for local use, e.g., temporary
   store on disk or in memory, use the native format since that is
   faster. For data that is going to be transfered to other machines
   (e.g., when writing data to the binary log), data should always be
   stored in little-endian format.

   @note The default method for packing fields just copy the raw bytes
   of the record into the destination, but never more than
   <code>max_length</code> characters.

   @param to
   Pointer to memory area where representation of field should be put.

   @param from
   Pointer to memory area where record representation of field is
   stored.

   @param max_length
   Maximum length of the field, as given in the column definition. For
   example, for <code>CHAR(1000)</code>, the <code>max_length</code>
   is 1000. This information is sometimes needed to decide how to pack
   the data.

   @param low_byte_first
   @c TRUE if integers should be stored little-endian, @c FALSE if
   native format should be used. Note that for little-endian machines,
   the value of this flag is a moot point since the native format is
   little-endian.
*/
uchar *Field::pack(uchar *to, const uchar *from, uint max_length,
                   bool low_byte_first __attribute__((unused)))
{
    uint32 length = pack_length();
    set_if_smaller(length, max_length);
    memcpy(to, from, length);
    return to + length;
}

/**
   Unpack a field from row data.

   This method is used to unpack a field from a master whose size of
   the field is less than that of the slave.

   The <code>param_data</code> parameter is a two-byte integer (stored
   in the least significant 16 bits of the unsigned integer) usually
   consisting of two parts: the real type in the most significant byte
   and a original pack length in the least significant byte.

   The exact layout of the <code>param_data</code> field is given by
   the <code>Table_map_log_event::save_field_metadata()</code>.

   This is the default method for unpacking a field. It just copies
   the memory block in byte order (of original pack length bytes or
   length of field, whichever is smaller).

   @param   to         Destination of the data
   @param   from       Source of the data
   @param   param_data Real type and original pack length of the field
                       data

   @param low_byte_first
   If this flag is @c true, all composite entities (e.g., lengths)
   should be unpacked in little-endian format; otherwise, the entities
   are unpacked in native order.

   @return  New pointer into memory based on from + length of the data
*/
const uchar *Field::unpack(uchar *to, const uchar *from, uint param_data,
                           bool low_byte_first __attribute__((unused)))
{
    uint length = pack_length();
    int from_type = 0;

    /*
      If from length is > 255, it has encoded data in the upper bits. Need
      to mask it out.
    */
    if (param_data > 255) {
        from_type = (param_data & 0xff00) >> 8U; // real_type.
        param_data = param_data & 0x00ff;       // length.
    }

    if ((param_data == 0) ||
            (length == param_data) ||
            (from_type != real_type())) {
        memcpy(to, from, length);
        return from + length;
    }

    uint len = (param_data && (param_data < length)) ?
               param_data : length;
    memcpy(to, from, param_data > length ? length : len);
    return from + len;
}


void Field_num::add_zerofill_and_unsigned(String &res) const
{
    if (unsigned_flag)
        res.append(STRING_WITH_LEN(" unsigned"));

    if (zerofill)
        res.append(STRING_WITH_LEN(" zerofill"));
}


void Field::make_field(Send_field *field)
{
    if (orig_table && orig_table->s->db.str && *orig_table->s->db.str) {
        field->db_name = orig_table->s->db.str;

        if (orig_table->pos_in_table_list &&
                orig_table->pos_in_table_list->schema_table)
            field->org_table_name = (orig_table->pos_in_table_list->
                                     schema_table->table_name);
        else
            field->org_table_name = orig_table->s->table_name.str;

    } else
        field->org_table_name = field->db_name = "";

    if (orig_table && orig_table->alias) {
        field->table_name = orig_table->alias;
        field->org_col_name = field_name;

    } else {
        field->table_name = "";
        field->org_col_name = "";
    }

    field->col_name = field_name;
    field->charsetnr = charset()->number;
    field->length = field_length;
    field->type = type();
    field->flags = table->maybe_null ? (flags & ~NOT_NULL_FLAG) : flags;
    field->decimals = decimals();
}


/**
  Conversion from decimal to longlong. Checks overflow and returns
  correct value (min/max) in case of overflow.

  @param val             value to be converted
  @param unsigned_flag   type of integer to which we convert val
  @param has_overflow    true if there is overflow

  @return
    value converted from val
*/
longlong Field::convert_decimal2longlong(const my_decimal *val,
        bool unsigned_flag,
        bool *has_overflow)
{
    if (unsigned_flag && val->sign()) {
        // Converting a signed decimal to unsigned int
        set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
        *has_overflow = true;
        return 0;
    }

    longlong val_ll;
    int conversion_error = my_decimal2int(E_DEC_ERROR & ~E_DEC_OVERFLOW
                                          & ~E_DEC_TRUNCATED,
                                          val, unsigned_flag, &val_ll);

    if (warn_if_overflow(conversion_error)) {
        *has_overflow = true;

        if (unsigned_flag)
            return ULONGLONG_MAX;

        return (val->sign() ? LONGLONG_MIN : LONGLONG_MAX);
    }

    return val_ll;
}


/**
  Storing decimal in integer fields.

  @param val       value for storing

  @note
    This method is used by all integer fields, real/decimal redefine it

  @retval TYPE_OK   Storage of value went fine without warnings or errors
  @retval !TYPE_OK  Warning/error as indicated by type_conversion_status enum
                    value
*/
type_conversion_status Field_num::store_decimal(const my_decimal *val)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    bool has_overflow = false;
    longlong i = convert_decimal2longlong(val, unsigned_flag, &has_overflow);
    const type_conversion_status res = store(i, unsigned_flag);
    return has_overflow ? TYPE_WARN_OUT_OF_RANGE : res;
}


/**
  Return decimal value of integer field.

  @param decimal_value     buffer for storing decimal value

  @note
    This method is used by all integer fields, real/decimal redefine it.
    All longlong values fit in our decimal buffer which cal store 8*9=72
    digits of integer number

  @return
    pointer to decimal buffer with value of field
*/

my_decimal *Field_num::val_decimal(my_decimal *decimal_value)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    DBUG_ASSERT(result_type() == INT_RESULT);
    longlong nr = val_int();
    int2my_decimal(E_DEC_FATAL_ERROR, nr, unsigned_flag, decimal_value);
    return decimal_value;
}


bool Field_num::get_date(MYSQL_TIME *ltime, uint fuzzydate)
{
    DBUG_ASSERT(result_type() == INT_RESULT);
    return my_longlong_to_datetime_with_warn(val_int(), ltime, fuzzydate);
}


bool Field_num::get_time(MYSQL_TIME *ltime)
{
    DBUG_ASSERT(result_type() == INT_RESULT);
    return my_longlong_to_time_with_warn(val_int(), ltime);
}


Field_str::Field_str(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
                     uchar null_bit_arg, utype unireg_check_arg,
                     const char *field_name_arg,
                     const CHARSET_INFO *charset_arg)
    : Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
            unireg_check_arg, field_name_arg)
{
    field_charset = charset_arg;

    if (charset_arg->state & MY_CS_BINSORT)
        flags |= BINARY_FLAG;

    field_derivation = DERIVATION_IMPLICIT;
}


void Field_str::make_field(Send_field *field)
{
    Field::make_field(field);
    field->decimals = 0;
}

/**
  Decimal representation of Field_str.

  @param d         value for storing

  @note
    Field_str is the base class for fields implemeting
    [VAR]CHAR, VAR[BINARY], BLOB/TEXT, GEOMETRY.
    String value should be converted to floating point value according
    our rules, so we use double to store value of decimal in string.

  @todo
    use decimal2string?

  @retval
    0     OK
  @retval
    !=0  error
*/

type_conversion_status Field_str::store_decimal(const my_decimal *d)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    double val;
    /* TODO: use decimal2string? */
    int err = my_decimal2double(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW, d, &val);
    warn_if_overflow(err);
    const type_conversion_status res = store(val);
    return (err != E_DEC_OK) ? decimal_err_to_type_conv_status(err) : res;
}


uint Field::fill_cache_field(CACHE_FIELD *copy)
{
    uint store_length;
    copy->str = ptr;
    copy->length = pack_length();
    copy->field = this;

    if (flags & BLOB_FLAG) {
        copy->type = CACHE_BLOB;
        copy->length -= portable_sizeof_char_ptr;
        return copy->length;

    } else if (!zero_pack() &&
               (type() == MYSQL_TYPE_STRING && copy->length >= 4 &&
                copy->length < 256)) {
        copy->type = CACHE_STRIPPED;			   /* Remove end space */
        store_length = 2;

    } else if (type() ==  MYSQL_TYPE_VARCHAR) {
        copy->type = pack_length() - row_pack_length() == 1 ? CACHE_VARSTR1 :
                     CACHE_VARSTR2;
        store_length = 0;

    } else {
        copy->type = 0;
        store_length = 0;
    }

    return copy->length + store_length;
}


bool Field::get_date(MYSQL_TIME *ltime, uint fuzzydate)
{
    char buff[MAX_DATE_STRING_REP_LENGTH];
    String tmp(buff, sizeof(buff), &my_charset_bin), *res;
    return !(res = val_str(&tmp)) ||
           str_to_datetime_with_warn(res, ltime, fuzzydate);
}


bool Field::get_time(MYSQL_TIME *ltime)
{
    char buff[MAX_DATE_STRING_REP_LENGTH];
    String tmp(buff, sizeof(buff), &my_charset_bin), *res;
    return !(res = val_str(&tmp)) || str_to_time_with_warn(res, ltime);
}


bool Field::get_timestamp(struct timeval *tm, int *warnings)
{
    MYSQL_TIME ltime;
    DBUG_ASSERT(!is_null());
    return get_date(&ltime, TIME_FUZZY_DATE) ||
           datetime_to_timeval(current_thd, &ltime, tm, warnings);
}


/**
  This is called when storing a date in a string.

  @note
    Needs to be changed if/when we want to support different time formats.
*/

type_conversion_status Field::store_time(MYSQL_TIME *ltime, uint8 dec_arg)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    char buff[MAX_DATE_STRING_REP_LENGTH];
    uint length = (uint) my_TIME_to_str(ltime, buff,
                                        MY_MIN(dec_arg, DATETIME_MAX_DECIMALS));
    /* Avoid conversion when field character set is ASCII compatible */
    return store(buff, length, (charset()->state & MY_CS_NONASCII) ?
                 &my_charset_latin1 : charset());
}


bool Field::optimize_range(uint idx, uint part)
{
    return test(table->file->index_flags(idx, part, 1) & HA_READ_RANGE);
}


Field *Field::new_field(MEM_ROOT *root, TABLE *new_table,
                        bool keep_type __attribute__((unused)))
{
    Field *tmp = clone(root);

    if (tmp == NULL)
        return 0;

    if (tmp->table->maybe_null)
        tmp->flags &= ~NOT_NULL_FLAG;

    tmp->table = new_table;
    tmp->key_start.init(0);
    tmp->part_of_key.init(0);
    tmp->part_of_sortkey.init(0);
    /*
      todo: We should never alter unireg_check after an object is constructed,
      and the member should be made const. But a lot of code depends upon this
      hack, and the different utype values are completely unrelated so we can
      never be quite sure which parts of the server will break.
    */
    tmp->unireg_check = Field::NONE;
    tmp->flags &= (NOT_NULL_FLAG | BLOB_FLAG | UNSIGNED_FLAG |
                   ZEROFILL_FLAG | BINARY_FLAG | ENUM_FLAG | SET_FLAG);
    tmp->reset_fields();
    return tmp;
}


Field *Field::new_key_field(MEM_ROOT *root, TABLE *new_table,
                            uchar *new_ptr, uchar *new_null_ptr,
                            uint new_null_bit)
{
    Field *tmp;

    if ((tmp = new_field(root, new_table, table == new_table))) {
        tmp->ptr =      new_ptr;
        tmp->null_ptr = new_null_ptr;
        tmp->null_bit = new_null_bit;
    }

    return tmp;
}

void Field::evaluate_insert_default_function()
{
    if (has_insert_default_function())
        Item_func_now_local::store_in(this);
}

void Field::evaluate_update_default_function()
{
    if (has_update_default_function())
        Item_func_now_local::store_in(this);
}

/****************************************************************************
  Field_null, a field that always return NULL
****************************************************************************/

void Field_null::sql_type(String &res) const
{
    res.set_ascii(STRING_WITH_LEN("null"));
}


/****************************************************************************
  Functions for the Field_decimal class
  This is an number stored as a pre-space (or pre-zero) string
****************************************************************************/

type_conversion_status Field_decimal::reset(void)
{
    Field_decimal::store(STRING_WITH_LEN("0"), &my_charset_bin);
    return TYPE_OK;
}

void Field_decimal::overflow(bool negative)
{
    uint len = field_length;
    uchar *to = ptr, filler = '9';
    set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);

    if (negative) {
        if (!unsigned_flag) {
            /* Put - sign as a first digit so we'll have -999..999 or 999..999 */
            *to++ = '-';
            len--;

        } else {
            filler = '0';				// Fill up with 0

            if (!zerofill) {
                /*
                  Handle unsigned integer without zerofill, in which case
                  the number should be of format '   0' or '   0.000'
                */
                uint whole_part = field_length - (dec ? dec + 2 : 1);
                // Fill with spaces up to the first digit
                memset(to, ' ', whole_part);
                to +=  whole_part;
                len -= whole_part;
                // The main code will also handle the 0 before the decimal point
            }
        }
    }

    memset(to, filler, len);

    if (dec)
        ptr[field_length - dec - 1] = '.';

    return;
}


type_conversion_status Field_decimal::store(const char *from_arg, uint len,
        const CHARSET_INFO *cs)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    char buff[STRING_BUFFER_USUAL_SIZE];
    String tmp(buff, sizeof(buff), &my_charset_bin);
    const uchar *from = (uchar *) from_arg;

    /* Convert character set if the old one is multi uchar */
    if (cs->mbmaxlen > 1) {
        uint dummy_errors;
        tmp.copy((char *) from, len, cs, &my_charset_bin, &dummy_errors);
        from = (uchar *) tmp.ptr();
        len =  tmp.length();
    }

    const uchar *end = from + len;
    /* The pointer where the field value starts (i.e., "where to write") */
    uchar *to = ptr;
    uint tmp_dec, tmp_uint;
    /*
      The sign of the number : will be 0 (means positive but sign not
      specified), '+' or '-'
    */
    uchar sign_char = 0;
    /* The pointers where prezeros start and stop */
    const uchar *pre_zeros_from, *pre_zeros_end;
    /* The pointers where digits at the left of '.' start and stop */
    const uchar *int_digits_from, *int_digits_end;
    /* The pointers where digits at the right of '.' start and stop */
    const uchar *frac_digits_from, *frac_digits_end;
    /* The sign of the exponent : will be 0 (means no exponent), '+' or '-' */
    char expo_sign_char = 0;
    uint exponent = 0;                              // value of the exponent
    /*
      Pointers used when digits move from the left of the '.' to the
      right of the '.' (explained below)
    */
    const uchar *UNINIT_VAR(int_digits_tail_from);
    /* Number of 0 that need to be added at the left of the '.' (1E3: 3 zeros) */
    uint UNINIT_VAR(int_digits_added_zeros);
    /*
      Pointer used when digits move from the right of the '.' to the left
      of the '.'
    */
    const uchar *UNINIT_VAR(frac_digits_head_end);
    /* Number of 0 that need to be added at the right of the '.' (for 1E-3) */
    uint UNINIT_VAR(frac_digits_added_zeros);
    uchar *pos, *tmp_left_pos, *tmp_right_pos;
    /* Pointers that are used as limits (begin and end of the field buffer) */
    uchar *left_wall, *right_wall;
    uchar tmp_char;
    /*
      To remember if table->in_use->cuted_fields has already been incremented,
      to do that only once
    */
    bool is_cuted_fields_incr = 0;

    /*
      There are three steps in this function :
      - parse the input string
      - modify the position of digits around the decimal dot '.'
        according to the exponent value (if specified)
      - write the formatted number
    */

    if ((tmp_dec = dec))
        tmp_dec++;

    /* skip pre-space */
    while (from != end && my_isspace(&my_charset_bin, *from))
        from++;

    if (from == end) {
        set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
        is_cuted_fields_incr = 1;

    } else if (*from == '+' || *from == '-') {	// Found some sign ?
        sign_char = *from++;

        /*
          We allow "+" for unsigned decimal unless defined different
          Both options allowed as one may wish not to have "+" for unsigned numbers
          because of data processing issues
        */
        if (unsigned_flag) {
            if (sign_char == '-') {
                Field_decimal::overflow(1);
                return TYPE_WARN_OUT_OF_RANGE;
            }

            /*
            Defining this will not store "+" for unsigned decimal type even if
             it is passed in numeric string. This will make some tests to fail
                */
#ifdef DONT_ALLOW_UNSIGNED_PLUS

            else
                sign_char = 0;

#endif
        }
    }

    pre_zeros_from = from;

    for (; from != end && *from == '0'; from++) ;	// Read prezeros

    pre_zeros_end = int_digits_from = from;

    /* Read non zero digits at the left of '.'*/
    for (; from != end && my_isdigit(&my_charset_bin, *from) ; from++) ;

    int_digits_end = from;

    if (from != end && *from == '.')		// Some '.' ?
        from++;

    frac_digits_from = from;

    /* Read digits at the right of '.' */
    for (; from != end && my_isdigit(&my_charset_bin, *from); from++) ;

    frac_digits_end = from;

    // Some exponentiation symbol ?
    if (from != end && (*from == 'e' || *from == 'E')) {
        from++;

        if (from != end && (*from == '+' || *from == '-'))  // Some exponent sign ?
            expo_sign_char = *from++;
        else
            expo_sign_char = '+';

        /*
          Read digits of the exponent and compute its value.  We must care about
          'exponent' overflow, because as unsigned arithmetic is "modulo", big
          exponents will become small (e.g. 1e4294967296 will become 1e0, and the
          field will finally contain 1 instead of its max possible value).
        */
        for (; from != end && my_isdigit(&my_charset_bin, *from); from++) {
            exponent = 10 * exponent + (*from - '0');

            if (exponent > MAX_EXPONENT)
                break;
        }
    }

    /*
      We only have to generate warnings if count_cuted_fields is set.
      This is to avoid extra checks of the number when they are not needed.
      Even if this flag is not set, it's OK to increment warnings, if
      it makes the code easer to read.
    */

    if (table->in_use->count_cuted_fields) {
        // Skip end spaces
        for (; from != end && my_isspace(&my_charset_bin, *from); from++) ;

        if (from != end) {                   // If still something left, warn
            set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
            is_cuted_fields_incr = 1;
        }
    }

    /*
      Now "move" digits around the decimal dot according to the exponent value,
      and add necessary zeros.
      Examples :
      - 1E+3 : needs 3 more zeros at the left of '.' (int_digits_added_zeros=3)
      - 1E-3 : '1' moves at the right of '.', and 2 more zeros are needed
      between '.' and '1'
      - 1234.5E-3 : '234' moves at the right of '.'
      These moves are implemented with pointers which point at the begin
      and end of each moved segment. Examples :
      - 1234.5E-3 : before the code below is executed, the int_digits part is
      from '1' to '4' and the frac_digits part from '5' to '5'. After the code
      below, the int_digits part is from '1' to '1', the frac_digits_head
      part is from '2' to '4', and the frac_digits part from '5' to '5'.
      - 1234.5E3 : before the code below is executed, the int_digits part is
      from '1' to '4' and the frac_digits part from '5' to '5'. After the code
      below, the int_digits part is from '1' to '4', the int_digits_tail
      part is from '5' to '5', the frac_digits part is empty, and
      int_digits_added_zeros=2 (to make 1234500).
    */

    /*
       Below tmp_uint cannot overflow with small enough MAX_EXPONENT setting,
       as int_digits_added_zeros<=exponent<4G and
       (int_digits_end-int_digits_from)<=max_allowed_packet<=2G and
       (frac_digits_from-int_digits_tail_from)<=max_allowed_packet<=2G
    */

    if (!expo_sign_char)
        tmp_uint = tmp_dec + (uint)(int_digits_end - int_digits_from);
    else if (expo_sign_char == '-') {
        tmp_uint = min(exponent, (uint)(int_digits_end - int_digits_from));
        frac_digits_added_zeros = exponent - tmp_uint;
        int_digits_end -= tmp_uint;
        frac_digits_head_end = int_digits_end + tmp_uint;
        tmp_uint = tmp_dec + (uint)(int_digits_end - int_digits_from);

    } else { // (expo_sign_char=='+')
        tmp_uint = min(exponent, (uint)(frac_digits_end - frac_digits_from));
        int_digits_added_zeros = exponent - tmp_uint;
        int_digits_tail_from = frac_digits_from;
        frac_digits_from = frac_digits_from + tmp_uint;

        /*
          We "eat" the heading zeros of the
          int_digits.int_digits_tail.int_digits_added_zeros concatenation
          (for example 0.003e3 must become 3 and not 0003)
        */
        if (int_digits_from == int_digits_end) {
            /*
            There was nothing in the int_digits part, so continue
            eating int_digits_tail zeros
                 */
            for (; int_digits_tail_from != frac_digits_from &&
                    *int_digits_tail_from == '0'; int_digits_tail_from++) ;

            if (int_digits_tail_from == frac_digits_from) {
                // there were only zeros in int_digits_tail too
                int_digits_added_zeros = 0;
            }
        }

        tmp_uint = (uint) (tmp_dec + (int_digits_end - int_digits_from) +
                           (uint)(frac_digits_from - int_digits_tail_from) +
                           int_digits_added_zeros);
    }

    /*
      Now write the formated number

      First the digits of the int_% parts.
      Do we have enough room to write these digits ?
      If the sign is defined and '-', we need one position for it
    */

    if (field_length < tmp_uint + (int) (sign_char == '-')) {
        // too big number, change to max or min number
        Field_decimal::overflow(sign_char == '-');
        return TYPE_WARN_OUT_OF_RANGE;
    }

    /*
      Tmp_left_pos is the position where the leftmost digit of
      the int_% parts will be written
    */
    tmp_left_pos = pos = to + (uint)(field_length - tmp_uint);

    // Write all digits of the int_% parts
    while (int_digits_from != int_digits_end)
        *pos++ = *int_digits_from++ ;

    if (expo_sign_char == '+') {
        while (int_digits_tail_from != frac_digits_from)
            *pos++ = *int_digits_tail_from++;

        while (int_digits_added_zeros-- > 0)
            *pos++ = '0';
    }

    /*
      Note the position where the rightmost digit of the int_% parts has been
      written (this is to later check if the int_% parts contained nothing,
      meaning an extra 0 is needed).
    */
    tmp_right_pos = pos;
    /*
      Step back to the position of the leftmost digit of the int_% parts,
      to write sign and fill with zeros or blanks or prezeros.
    */
    pos = tmp_left_pos - 1;

    if (zerofill) {
        left_wall = to - 1;

        while (pos > left_wall)			// Fill with zeros
            *pos-- = '0';

    } else {
        left_wall = to + (sign_char != 0) - 1;

        if (!expo_sign_char) {	// If exponent was specified, ignore prezeros
            for (; pos > left_wall && pre_zeros_from != pre_zeros_end;
                    pre_zeros_from++)
                *pos-- = '0';
        }

        if (pos == tmp_right_pos - 1)
            *pos-- = '0';		// no 0 has ever been written, so write one

        left_wall = to - 1;

        if (sign_char && pos != left_wall) {
            /* Write sign if possible (it is if sign is '-') */
            *pos-- = sign_char;
        }

        while (pos != left_wall)
            *pos-- = ' '; //fill with blanks
    }

    /*
      Write digits of the frac_% parts ;
      Depending on table->in_use->count_cutted_fields, we may also want
      to know if some non-zero tail of these parts will
      be truncated (for example, 0.002->0.00 will generate a warning,
      while 0.000->0.00 will not)
      (and 0E1000000000 will not, while 1E-1000000000 will)
    */
    pos = to + (uint)(field_length - tmp_dec);	// Calculate post to '.'
    right_wall = to + field_length;

    if (pos != right_wall)
        *pos++ = '.';

    if (expo_sign_char == '-') {
        while (frac_digits_added_zeros-- > 0) {
            if (pos == right_wall) {
                if (table->in_use->count_cuted_fields && !is_cuted_fields_incr)
                    break; // Go on below to see if we lose non zero digits

                return TYPE_OK;
            }

            *pos++ = '0';
        }

        while (int_digits_end != frac_digits_head_end) {
            tmp_char = *int_digits_end++;

            if (pos == right_wall) {
                if (tmp_char != '0') {		// Losing a non zero digit ?
                    if (!is_cuted_fields_incr)
                        set_warning(Sql_condition::WARN_LEVEL_WARN,
                                    WARN_DATA_TRUNCATED, 1);

                    return TYPE_OK;
                }

                continue;
            }

            *pos++ = tmp_char;
        }
    }

    for (; frac_digits_from != frac_digits_end;) {
        tmp_char = *frac_digits_from++;

        if (pos == right_wall) {
            if (tmp_char != '0') {		// Losing a non zero digit ?
                if (!is_cuted_fields_incr) {
                    /*
                      This is a note, not a warning, as we don't want to abort
                      when we cut decimals in strict mode
                    */
                    set_warning(Sql_condition::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED, 1);
                }

                return TYPE_OK;
            }

            continue;
        }

        *pos++ = tmp_char;
    }

    while (pos != right_wall)
        *pos++ = '0';			// Fill with zeros at right of '.'

    return TYPE_OK;
}


type_conversion_status Field_decimal::store(double nr)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;

    if (unsigned_flag && nr < 0) {
        overflow(1);
        return TYPE_WARN_OUT_OF_RANGE;
    }

    if (!isfinite(nr)) { // Handle infinity as special case
        overflow(nr < 0.0);
        return TYPE_WARN_OUT_OF_RANGE;
    }

    reg4 uint i;
    size_t length;
    uchar fyllchar, *to;
    char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE];
    fyllchar = zerofill ? (char) '0' : (char) ' ';
    length = my_fcvt(nr, dec, buff, NULL);

    if (length > field_length) {
        overflow(nr < 0.0);
        return TYPE_WARN_OUT_OF_RANGE;

    } else {
        to = ptr;

        for (i = field_length - length ; i-- > 0 ;)
            *to++ = fyllchar;

        memcpy(to, buff, length);
        return TYPE_OK;
    }
}


type_conversion_status Field_decimal::store(longlong nr, bool unsigned_val)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    char buff[22];
    uint length, int_part;
    char fyllchar;
    uchar *to;

    if (nr < 0 && unsigned_flag && !unsigned_val) {
        overflow(1);
        return TYPE_WARN_OUT_OF_RANGE;
    }

    length = (uint) (longlong10_to_str(nr, buff, unsigned_val ? 10 : -10) - buff);
    int_part = field_length - (dec  ? dec + 1 : 0);

    if (length > int_part) {
        overflow(!unsigned_val && nr < 0L);		/* purecov: inspected */
        return TYPE_WARN_OUT_OF_RANGE;
    }

    fyllchar = zerofill ? (char) '0' : (char) ' ';
    to = ptr;

    for (uint i = int_part - length ; i-- > 0 ;)
        *to++ = fyllchar;

    memcpy(to, buff, length);

    if (dec) {
        to[length] = '.';
        memset(to + length + 1, '0', dec);
    }

    return TYPE_OK;
}


double Field_decimal::val_real(void)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    int not_used;
    char *end_not_used;
    return my_strntod(&my_charset_bin, (char *) ptr, field_length, &end_not_used,
                      &not_used);
}

longlong Field_decimal::val_int(void)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    int not_used;

    if (unsigned_flag)
        return my_strntoull(&my_charset_bin, (char *) ptr, field_length, 10, NULL,
                            &not_used);

    return my_strntoll(&my_charset_bin, (char *) ptr, field_length, 10, NULL,
                       &not_used);
}


String *Field_decimal::val_str(String *val_buffer __attribute__((unused)),
                               String *val_ptr)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    uchar *str;
    size_t tmp_length;

    for (str = ptr ; *str == ' ' ; str++) ;

    val_ptr->set_charset(&my_charset_numeric);
    tmp_length = (size_t) (str - ptr);

    if (field_length < tmp_length)		// Error in data
        val_ptr->length(0);
    else
        val_ptr->set_ascii((const char *) str, field_length - tmp_length);

    return val_ptr;
}

/**
  Should be able to handle at least the following fixed decimal formats:
  5.00 , -1.0,  05,  -05, +5 with optional pre/end space
*/

int Field_decimal::cmp(const uchar *a_ptr, const uchar *b_ptr)
{
    const uchar *end;
    int swap = 0;

    /* First remove prefixes '0', ' ', and '-' */
    for (end = a_ptr + field_length;
            a_ptr != end &&
            (*a_ptr == *b_ptr ||
             ((my_isspace(&my_charset_bin, *a_ptr)  || *a_ptr == '+' ||
               *a_ptr == '0') &&
              (my_isspace(&my_charset_bin, *b_ptr) || *b_ptr == '+' ||
               *b_ptr == '0')));
            a_ptr++, b_ptr++) {
        if (*a_ptr == '-')				// If both numbers are negative
            swap = -1 ^ 1;				// Swap result
    }

    if (a_ptr == end)
        return 0;

    if (*a_ptr == '-')
        return -1;

    if (*b_ptr == '-')
        return 1;

    while (a_ptr != end) {
        if (*a_ptr++ != *b_ptr++)
            return swap ^ (a_ptr[-1] < b_ptr[-1] ? -1 : 1); // compare digits
    }

    return 0;
}


void Field_decimal::make_sort_key(uchar *to, uint length)
{
    uchar *str, *end;

    for (str = ptr, end = ptr + length;
            str != end &&
            ((my_isspace(&my_charset_bin, *str) || *str == '+' ||
              *str == '0')) ;
            str++)
        *to++ = ' ';

    if (str == end)
        return;					/* purecov: inspected */

    if (*str == '-') {
        *to++ = 1;					// Smaller than any number
        str++;

        while (str != end)
            if (my_isdigit(&my_charset_bin, *str))
                *to++ = (char) ('9' - *str++);
            else
                *to++ = *str++;

    } else
        memcpy(to, str, (uint) (end - str));
}


void Field_decimal::sql_type(String &res) const
{
    const CHARSET_INFO *cs = res.charset();
    uint tmp = field_length;

    if (!unsigned_flag)
        tmp--;

    if (dec)
        tmp--;

    res.length(cs->cset->snprintf(cs, (char *) res.ptr(), res.alloced_length(),
                                  "decimal(%d,%d)", tmp, dec));
    add_zerofill_and_unsigned(res);
}


/****************************************************************************
** Field_new_decimal
****************************************************************************/

Field_new_decimal::Field_new_decimal(uchar *ptr_arg,
                                     uint32 len_arg, uchar *null_ptr_arg,
                                     uchar null_bit_arg,
                                     enum utype unireg_check_arg,
                                     const char *field_name_arg,
                                     uint8 dec_arg, bool zero_arg,
                                     bool unsigned_arg)
    : Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
                unireg_check_arg, field_name_arg, dec_arg, zero_arg, unsigned_arg)
{
    precision = my_decimal_length_to_precision(len_arg, dec_arg, unsigned_arg);
    set_if_smaller(precision, DECIMAL_MAX_PRECISION);
    DBUG_ASSERT((precision <= DECIMAL_MAX_PRECISION) &&
                (dec <= DECIMAL_MAX_SCALE));
    bin_size = my_decimal_get_binary_size(precision, dec);
}


Field_new_decimal::Field_new_decimal(uint32 len_arg,
                                     bool maybe_null_arg,
                                     const char *name,
                                     uint8 dec_arg,
                                     bool unsigned_arg)
    : Field_num((uchar *) 0, len_arg,
                maybe_null_arg ? (uchar *) "" : 0, 0,
                NONE, name, dec_arg, 0, unsigned_arg)
{
    precision = my_decimal_length_to_precision(len_arg, dec_arg, unsigned_arg);
    set_if_smaller(precision, DECIMAL_MAX_PRECISION);
    DBUG_ASSERT((precision <= DECIMAL_MAX_PRECISION) &&
                (dec <= DECIMAL_MAX_SCALE));
    bin_size = my_decimal_get_binary_size(precision, dec);
}


Field *Field_new_decimal::create_from_item (Item *item)
{
    uint8 dec = item->decimals;
    uint8 intg = item->decimal_precision() - dec;
    uint32 len = item->max_char_length();
    DBUG_ASSERT (item->result_type() == DECIMAL_RESULT);

    /*
      Trying to put too many digits overall in a DECIMAL(prec,dec)
      will always throw a warning. We must limit dec to
      DECIMAL_MAX_SCALE however to prevent an assert() later.
    */

    if (dec > 0) {
        signed int overflow;
        dec = min<int>(dec, DECIMAL_MAX_SCALE);
        /*
          If the value still overflows the field with the corrected dec,
          we'll throw out decimals rather than integers. This is still
          bad and of course throws a truncation warning.
          +1: for decimal point
          */
        const int required_length =
            my_decimal_precision_to_length(intg + dec, dec,
                                           item->unsigned_flag);
        overflow = required_length - len;

        if (overflow > 0)
            dec = max(0, dec - overflow);           // too long, discard fract
        else
            /* Corrected value fits. */
            len = required_length;
    }

    return new Field_new_decimal(len, item->maybe_null, item->item_name.ptr(),
                                 dec, item->unsigned_flag);
}


type_conversion_status Field_new_decimal::reset(void)
{
    store_value(&decimal_zero);
    return TYPE_OK;
}


/**
  Generate max/min decimal value in case of overflow.

  @param decimal_value     buffer for value
  @param sign              sign of value which caused overflow
*/

void Field_new_decimal::set_value_on_overflow(my_decimal *decimal_value,
        bool sign)
{
    DBUG_ENTER("Field_new_decimal::set_value_on_overflow");
    max_my_decimal(decimal_value, precision, decimals());

    if (sign) {
        if (unsigned_flag)
            my_decimal_set_zero(decimal_value);
        else
            decimal_value->sign(TRUE);
    }

    DBUG_VOID_RETURN;
}


/**
  Store decimal value in the binary buffer.

  Checks if decimal_value fits into field size.
  If it does, stores the decimal in the buffer using binary format.
  Otherwise sets maximal number that can be stored in the field.

  @param decimal_value   my_decimal

  @retval
    0 ok
  @retval
    1 error
*/
type_conversion_status Field_new_decimal::store_value(const my_decimal *decimal_value)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    type_conversion_status error = TYPE_OK;
    DBUG_ENTER("Field_new_decimal::store_value");
#ifndef DBUG_OFF
    {
        char dbug_buff[DECIMAL_MAX_STR_LENGTH + 2];
        DBUG_PRINT("enter", ("value: %s", dbug_decimal_as_string(dbug_buff, decimal_value)));
    }
#endif

    /* check that we do not try to write negative value in unsigned field */
    if (unsigned_flag && decimal_value->sign()) {
        DBUG_PRINT("info", ("unsigned overflow"));
        set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
        error = TYPE_WARN_OUT_OF_RANGE;
        decimal_value = &decimal_zero;
    }

#ifndef DBUG_OFF
    {
        char dbug_buff[DECIMAL_MAX_STR_LENGTH + 2];
        DBUG_PRINT("info", ("saving with precision %d  scale: %d  value %s",
                            (int)precision, (int)dec,
                            dbug_decimal_as_string(dbug_buff, decimal_value)));
    }
#endif
    int err = my_decimal2binary(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW,
                                decimal_value, ptr, precision, dec);

    if (warn_if_overflow(err)) {
        my_decimal buff;
        DBUG_PRINT("info", ("overflow"));
        set_value_on_overflow(&buff, decimal_value->sign());
        my_decimal2binary(E_DEC_FATAL_ERROR, &buff, ptr, precision, dec);
    }

    DBUG_EXECUTE("info", print_decimal_buff(decimal_value, (uchar *) ptr,
                                            bin_size););
    DBUG_RETURN((err != E_DEC_OK) ? decimal_err_to_type_conv_status(err)
                : error);
}


type_conversion_status Field_new_decimal::store(const char *from, uint length,
        const CHARSET_INFO *charset_arg)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    my_decimal decimal_value;
    DBUG_ENTER("Field_new_decimal::store(char*)");
    int err = str2my_decimal(E_DEC_FATAL_ERROR &
                             ~(E_DEC_OVERFLOW | E_DEC_BAD_NUM),
                             from, length, charset_arg,
                             &decimal_value);

    if (err != 0 && table->in_use->abort_on_warning) {
        ErrConvString errmsg(from, length, charset_arg);
        const Diagnostics_area *da = table->in_use->get_stmt_da();
        push_warning_printf(table->in_use, Sql_condition::WARN_LEVEL_WARN,
                            ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
                            ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
                            "decimal", errmsg.ptr(), field_name,
                            static_cast<ulong>(da->current_row_for_warning()));
        DBUG_RETURN(decimal_err_to_type_conv_status(err));
    }

    if (err != 0)
        set_decimal_warning(this, err, &decimal_value, from, length, charset_arg);

#ifndef DBUG_OFF
    char dbug_buff[DECIMAL_MAX_STR_LENGTH + 2];
    DBUG_PRINT("enter", ("value: %s",
                         dbug_decimal_as_string(dbug_buff, &decimal_value)));
#endif
    type_conversion_status store_stat = store_value(&decimal_value);
    DBUG_RETURN(err != 0 ? decimal_err_to_type_conv_status(err) : store_stat);
}


type_conversion_status store_internal_with_error_check(Field_new_decimal *field,
        int err, my_decimal *value)
{
    type_conversion_status stat = TYPE_OK;

    if (err != 0) {
        if (field->check_overflow(err)) {
            field->set_value_on_overflow(value, value->sign());
            stat = TYPE_WARN_OUT_OF_RANGE;

        } else if (field->check_truncated(err))
            stat = TYPE_NOTE_TRUNCATED;

        /* Only issue a warning if store_value doesn't issue an warning */
        field->table->in_use->got_warning = 0;
    }

    type_conversion_status store_stat = field->store_value(value);

    if (store_stat != TYPE_OK)
        return store_stat;
    else if (err != 0 && !field->table->in_use->got_warning)
        field->warn_if_overflow(err);

    return stat;
}

/**
  @todo
  Fix following when double2my_decimal when double2decimal
  will return E_DEC_TRUNCATED always correctly
*/

type_conversion_status Field_new_decimal::store(double nr)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    DBUG_ENTER("Field_new_decimal::store(double)");
    my_decimal decimal_value;
    int conv_err = double2my_decimal(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW, nr,
                                     &decimal_value);
    DBUG_RETURN(store_internal_with_error_check(this, conv_err, &decimal_value));
}

type_conversion_status Field_new_decimal::store(longlong nr, bool unsigned_val)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    DBUG_ENTER("Field_new_decimal::store(double, unsigned_val)");
    my_decimal decimal_value;
    int conv_err = int2my_decimal(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW,
                                  nr, unsigned_val, &decimal_value);
    DBUG_RETURN(store_internal_with_error_check(this, conv_err, &decimal_value));
}


type_conversion_status Field_new_decimal::store_decimal(const my_decimal *decimal_value)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    return store_value(decimal_value);
}


type_conversion_status Field_new_decimal::store_time(MYSQL_TIME *ltime,
        uint8 dec_arg __attribute__((unused)))
{
    my_decimal decimal_value;
    return store_value(date2my_decimal(ltime, &decimal_value));
}


double Field_new_decimal::val_real(void)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    double dbl;
    my_decimal decimal_value;
    my_decimal2double(E_DEC_FATAL_ERROR, val_decimal(&decimal_value), &dbl);
    return dbl;
}


longlong Field_new_decimal::val_int(void)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    longlong i;
    my_decimal decimal_value;
    my_decimal2int(E_DEC_FATAL_ERROR, val_decimal(&decimal_value),
                   unsigned_flag, &i);
    return i;
}


my_decimal *Field_new_decimal::val_decimal(my_decimal *decimal_value)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    DBUG_ENTER("Field_new_decimal::val_decimal");
    binary2my_decimal(E_DEC_FATAL_ERROR, ptr, decimal_value,
                      precision, dec);
    DBUG_EXECUTE("info", print_decimal_buff(decimal_value, (uchar *) ptr,
                                            bin_size););
    DBUG_RETURN(decimal_value);
}


String *Field_new_decimal::val_str(String *val_buffer,
                                   String *val_ptr __attribute__((unused)))
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    my_decimal decimal_value;
    uint fixed_precision = zerofill ? precision : 0;
    my_decimal2string(E_DEC_FATAL_ERROR, val_decimal(&decimal_value),
                      fixed_precision, dec, '0', val_buffer);
    val_buffer->set_charset(&my_charset_numeric);
    return val_buffer;
}


bool Field_new_decimal::get_date(MYSQL_TIME *ltime, uint fuzzydate)
{
    my_decimal buf, *decimal_value = val_decimal(&buf);

    if (!decimal_value) {
        set_zero_time(ltime, MYSQL_TIMESTAMP_DATETIME);
        return true;
    }

    return my_decimal_to_datetime_with_warn(decimal_value, ltime, fuzzydate);
}


bool Field_new_decimal::get_time(MYSQL_TIME *ltime)
{
    my_decimal buf, *decimal_value = val_decimal(&buf);

    if (!decimal_value) {
        set_zero_time(ltime, MYSQL_TIMESTAMP_TIME);
        return true;
    }

    return my_decimal_to_time_with_warn(decimal_value, ltime);
}


int Field_new_decimal::cmp(const uchar *a, const uchar *b)
{
    return memcmp(a, b, bin_size);
}


void Field_new_decimal::make_sort_key(uchar *buff, uint length)
{
    memcpy(buff, ptr, min<uint>(length, bin_size));
}


void Field_new_decimal::sql_type(String &str) const
{
    const CHARSET_INFO *cs = str.charset();
    str.length(cs->cset->snprintf(cs, (char *) str.ptr(), str.alloced_length(),
                                  "decimal(%d,%d)", precision, (int)dec));
    add_zerofill_and_unsigned(str);
}


/**
   Save the field metadata for new decimal fields.

   Saves the precision in the first byte and decimals() in the second
   byte of the field metadata array at index of *metadata_ptr and
   *(metadata_ptr + 1).

   @param   metadata_ptr   First byte of field metadata

   @returns number of bytes written to metadata_ptr
*/
int Field_new_decimal::do_save_field_metadata(uchar *metadata_ptr)
{
    *metadata_ptr = precision;
    *(metadata_ptr + 1) = decimals();
    return 2;
}


/**
   Returns the number of bytes field uses in row-based replication
   row packed size.

   This method is used in row-based replication to determine the number
   of bytes that the field consumes in the row record format. This is
   used to skip fields in the master that do not exist on the slave.

   @param   field_metadata   Encoded size in field metadata

   @returns The size of the field based on the field metadata.
*/
uint Field_new_decimal::pack_length_from_metadata(uint field_metadata)
{
    uint const source_precision = (field_metadata >> 8U) & 0x00ff;
    uint const source_decimal = field_metadata & 0x00ff;
    uint const source_size = my_decimal_get_binary_size(source_precision,
                             source_decimal);
    return (source_size);
}

/**
   Check to see if field size is compatible with destination.

   This method is used in row-based replication to verify that the slave's
   field size is less than or equal to the master's field size. The
   encoded field metadata (from the master or source) is decoded and compared
   to the size of this field (the slave or destination).

   @param   field_metadata   Encoded size in field metadata
   @param   order_var        Pointer to variable where the order
                             between the source field and this field
                             will be returned.

   @return @c true
*/
bool Field_new_decimal::compatible_field_size(uint field_metadata,
        Relay_log_info *__attribute__((unused)),
        uint16 mflags __attribute__((unused)),
        int *order_var)
{
    uint const source_precision = (field_metadata >> 8U) & 0x00ff;
    uint const source_decimal = field_metadata & 0x00ff;
    int order = compare(source_precision, precision);
    *order_var = order != 0 ? order : compare(source_decimal, dec);
    return true;
}


uint Field_new_decimal::is_equal(Create_field *new_field)
{
    return ((new_field->sql_type == real_type()) &&
            ((new_field->flags & UNSIGNED_FLAG) ==
             (uint) (flags & UNSIGNED_FLAG)) &&
            ((new_field->flags & AUTO_INCREMENT_FLAG) ==
             (uint) (flags & AUTO_INCREMENT_FLAG)) &&
            (new_field->length == max_display_length()) &&
            (new_field->decimals == dec));
}


/**
   Unpack a decimal field from row data.

   This method is used to unpack a decimal or numeric field from a master
   whose size of the field is less than that of the slave.

   @param   to         Destination of the data
   @param   from       Source of the data
   @param   param_data Precision (upper) and decimal (lower) values

   @return  New pointer into memory based on from + length of the data
*/
const uchar *Field_new_decimal::unpack(uchar *to,
                                       const uchar *from,
                                       uint param_data,
                                       bool low_byte_first)
{
    if (param_data == 0)
        return Field::unpack(to, from, param_data, low_byte_first);

    uint from_precision = (param_data & 0xff00) >> 8U;
    uint from_decimal = param_data & 0x00ff;
    uint length = pack_length();
    uint from_pack_len = my_decimal_get_binary_size(from_precision, from_decimal);
    uint len = (param_data && (from_pack_len < length)) ?
               from_pack_len : length;

    if ((from_pack_len && (from_pack_len < length)) ||
            (from_precision < precision) ||
            (from_decimal < decimals())) {
        /*
          If the master's data is smaller than the slave, we need to convert
          the binary to decimal then resize the decimal converting it back to
          a decimal and write that to the raw data buffer.
        */
        decimal_digit_t dec_buf[DECIMAL_MAX_PRECISION];
        decimal_t dec_val;
        dec_val.len = from_precision;
        dec_val.buf = dec_buf;
        /*
          Note: bin2decimal does not change the length of the field. So it is
          just the first step the resizing operation. The second step does the
          resizing using the precision and decimals from the slave.
        */
        bin2decimal((uchar *)from, &dec_val, from_precision, from_decimal);
        decimal2bin(&dec_val, to, precision, decimals());

    } else
        memcpy(to, from, len); // Sizes are the same, just copy the data.

    return from + len;
}

/****************************************************************************
** tiny int
****************************************************************************/

type_conversion_status Field_tiny::store(const char *from, uint len, const CHARSET_INFO *cs)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    longlong rnd;
    const type_conversion_status error = get_int(cs, from, len, &rnd,
                                         255, -128, 127);
    ptr[0] = unsigned_flag ? (char) (ulonglong) rnd : (char) rnd;
    return error;
}


type_conversion_status Field_tiny::store(double nr)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    type_conversion_status error = TYPE_OK;
    nr = rint(nr);

    if (unsigned_flag) {
        if (nr < 0.0) {
            *ptr = 0;
            set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
            error = TYPE_WARN_OUT_OF_RANGE;

        } else if (nr > 255.0) {
            *ptr = (char) 255;
            set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
            error = TYPE_WARN_OUT_OF_RANGE;

        } else
            *ptr = (char) nr;

    } else {
        if (nr < -128.0) {
            *ptr = (char) -128;
            set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
            error = TYPE_WARN_OUT_OF_RANGE;

        } else if (nr > 127.0) {
            *ptr = 127;
            set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
            error = TYPE_WARN_OUT_OF_RANGE;

        } else
            *ptr = (char) (int) nr;
    }

    return error;
}


type_conversion_status Field_tiny::store(longlong nr, bool unsigned_val)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    type_conversion_status error = TYPE_OK;

    if (unsigned_flag) {
        if (nr < 0 && !unsigned_val) {
            *ptr = 0;
            set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
            error = TYPE_WARN_OUT_OF_RANGE;

        } else if ((ulonglong) nr > (ulonglong) 255) {
            *ptr = (char) 255;
            set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
            error = TYPE_WARN_OUT_OF_RANGE;

        } else
            *ptr = (char) nr;

    } else {
        if (nr < 0 && unsigned_val)
            nr = 256;                                   // Generate overflow

        if (nr < -128) {
            *ptr = (char) -128;
            set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
            error = TYPE_WARN_OUT_OF_RANGE;

        } else if (nr > 127) {
            *ptr = 127;
            set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
            error = TYPE_WARN_OUT_OF_RANGE;

        } else
            *ptr = (char) nr;
    }

    return error;
}


double Field_tiny::val_real(void)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    int tmp = unsigned_flag ? (int) ptr[0] :
              (int) ((signed char *) ptr)[0];
    return (double) tmp;
}


longlong Field_tiny::val_int(void)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    int tmp = unsigned_flag ? (int) ptr[0] :
              (int) ((signed char *) ptr)[0];
    return (longlong) tmp;
}


String *Field_tiny::val_str(String *val_buffer,
                            String *val_ptr __attribute__((unused)))
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    const CHARSET_INFO *cs = &my_charset_numeric;
    uint length;
    uint mlength = max(field_length + 1, 5 * cs->mbmaxlen);
    val_buffer->alloc(mlength);
    char *to = (char *) val_buffer->ptr();

    if (unsigned_flag)
        length = (uint) cs->cset->long10_to_str(cs, to, mlength, 10,
                                                (long) * ptr);
    else
        length = (uint) cs->cset->long10_to_str(cs, to, mlength, -10,
                                                (long) * ((signed char *) ptr));

    val_buffer->length(length);

    if (zerofill)
        prepend_zeros(val_buffer);

    val_buffer->set_charset(cs);
    return val_buffer;
}

bool Field_tiny::send_binary(Protocol *protocol)
{
    return protocol->store_tiny((longlong) (int8) ptr[0]);
}

int Field_tiny::cmp(const uchar *a_ptr, const uchar *b_ptr)
{
    signed char a, b;
    a = (signed char) a_ptr[0];
    b = (signed char) b_ptr[0];

    if (unsigned_flag)
        return ((uchar) a < (uchar) b) ? -1 : ((uchar) a > (uchar) b) ? 1 : 0;

    return (a < b) ? -1 : (a > b) ? 1 : 0;
}

void Field_tiny::make_sort_key(uchar *to, uint length)
{
    DBUG_ASSERT(length >= 1);

    if (unsigned_flag)
        *to = *ptr;
    else
        to[0] = (char) (ptr[0] ^ (uchar) 128);	/* Revers signbit */
}

void Field_tiny::sql_type(String &res) const
{
    const CHARSET_INFO *cs = res.charset();
    res.length(cs->cset->snprintf(cs, (char *) res.ptr(), res.alloced_length(),
                                  "tinyint(%d)", (int) field_length));
    add_zerofill_and_unsigned(res);
}

/****************************************************************************
 Field type short int (2 byte)
****************************************************************************/

type_conversion_status Field_short::store(const char *from, uint len, const CHARSET_INFO *cs)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    int store_tmp;
    longlong rnd;
    const type_conversion_status error =
        get_int(cs, from, len, &rnd, UINT_MAX16, INT_MIN16, INT_MAX16);
    store_tmp = unsigned_flag ? (int) (ulonglong) rnd : (int) rnd;
#ifdef WORDS_BIGENDIAN

    if (table->s->db_low_byte_first)
        int2store(ptr, store_tmp);

    else
#endif
        shortstore(ptr, (short) store_tmp);

    return error;
}


type_conversion_status Field_short::store(double nr)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    type_conversion_status error = TYPE_OK;
    int16 res;
    nr = rint(nr);

    if (unsigned_flag) {
        if (nr < 0) {
            res = 0;
            set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
            error = TYPE_WARN_OUT_OF_RANGE;

        } else if (nr > (double) UINT_MAX16) {
            res = (int16) UINT_MAX16;
            set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
            error = TYPE_WARN_OUT_OF_RANGE;

        } else
            res = (int16) (uint16) nr;

    } else {
        if (nr < (double) INT_MIN16) {
            res = INT_MIN16;
            set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
            error = TYPE_WARN_OUT_OF_RANGE;

        } else if (nr > (double) INT_MAX16) {
            res = INT_MAX16;
            set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
            error = TYPE_WARN_OUT_OF_RANGE;

        } else
            res = (int16) (int) nr;
    }

#ifdef WORDS_BIGENDIAN

    if (table->s->db_low_byte_first)
        int2store(ptr, res);

    else
#endif
        shortstore(ptr, res);

    return error;
}


type_conversion_status Field_short::store(longlong nr, bool unsigned_val)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    type_conversion_status error = TYPE_OK;
    int16 res;

    if (unsigned_flag) {
        if (nr < 0L && !unsigned_val) {
            res = 0;
            set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
            error = TYPE_WARN_OUT_OF_RANGE;

        } else if ((ulonglong) nr > (ulonglong) UINT_MAX16) {
            res = (int16) UINT_MAX16;
            set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
            error = TYPE_WARN_OUT_OF_RANGE;

        } else
            res = (int16) (uint16) nr;

    } else {
        if (nr < 0 && unsigned_val)
            nr = UINT_MAX16 + 1;                      // Generate overflow

        if (nr < INT_MIN16) {
            res = INT_MIN16;
            set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
            error = TYPE_WARN_OUT_OF_RANGE;

        } else if (nr > (longlong) INT_MAX16) {
            res = INT_MAX16;
            set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
            error = TYPE_WARN_OUT_OF_RANGE;

        } else
            res = (int16) nr;
    }

#ifdef WORDS_BIGENDIAN

    if (table->s->db_low_byte_first)
        int2store(ptr, res);

    else
#endif
        shortstore(ptr, res);

    return error;
}


double Field_short::val_real(void)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    short j;
#ifdef WORDS_BIGENDIAN

    if (table->s->db_low_byte_first)
        j = sint2korr(ptr);
    else
#endif
        shortget(j, ptr);

    return unsigned_flag ? (double) (unsigned short) j : (double) j;
}

longlong Field_short::val_int(void)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    short j;
#ifdef WORDS_BIGENDIAN

    if (table->s->db_low_byte_first)
        j = sint2korr(ptr);
    else
#endif
        shortget(j, ptr);

    return unsigned_flag ? (longlong) (unsigned short) j : (longlong) j;
}


String *Field_short::val_str(String *val_buffer,
                             String *val_ptr __attribute__((unused)))
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    const CHARSET_INFO *cs = &my_charset_numeric;
    uint length;
    uint mlength = max(field_length + 1, 7 * cs->mbmaxlen);
    val_buffer->alloc(mlength);
    char *to = (char *) val_buffer->ptr();
    short j;
#ifdef WORDS_BIGENDIAN

    if (table->s->db_low_byte_first)
        j = sint2korr(ptr);
    else
#endif
        shortget(j, ptr);

    if (unsigned_flag)
        length = (uint) cs->cset->long10_to_str(cs, to, mlength, 10,
                                                (long) (uint16) j);
    else
        length = (uint) cs->cset->long10_to_str(cs, to, mlength, -10, (long) j);

    val_buffer->length(length);

    if (zerofill)
        prepend_zeros(val_buffer);

    val_buffer->set_charset(cs);
    return val_buffer;
}


bool Field_short::send_binary(Protocol *protocol)
{
    return protocol->store_short(Field_short::val_int());
}


int Field_short::cmp(const uchar *a_ptr, const uchar *b_ptr)
{
    short a, b;
#ifdef WORDS_BIGENDIAN

    if (table->s->db_low_byte_first) {
        a = sint2korr(a_ptr);
        b = sint2korr(b_ptr);

    } else
#endif
    {
        shortget(a, a_ptr);
        shortget(b, b_ptr);
    }

    if (unsigned_flag)
        return ((unsigned short) a < (unsigned short) b) ? -1 :
               ((unsigned short) a > (unsigned short) b) ? 1 : 0;

    return (a < b) ? -1 : (a > b) ? 1 : 0;
}

void Field_short::make_sort_key(uchar *to, uint length)
{
    DBUG_ASSERT(length >= 2);
#ifdef WORDS_BIGENDIAN

    if (!table->s->db_low_byte_first) {
        if (unsigned_flag)
            to[0] = ptr[0];
        else
            to[0] = (char) (ptr[0] ^ 128);		/* Revers signbit */

        to[1]   = ptr[1];

    } else
#endif
    {
        if (unsigned_flag)
            to[0] = ptr[1];
        else
            to[0] = (char) (ptr[1] ^ 128);		/* Revers signbit */

        to[1]   = ptr[0];
    }
}

void Field_short::sql_type(String &res) const
{
    const CHARSET_INFO *cs = res.charset();
    res.length(cs->cset->snprintf(cs, (char *) res.ptr(), res.alloced_length(),
                                  "smallint(%d)", (int) field_length));
    add_zerofill_and_unsigned(res);
}


/****************************************************************************
  Field type medium int (3 byte)
****************************************************************************/

type_conversion_status Field_medium::store(const char *from, uint len,
        const CHARSET_INFO *cs)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    int store_tmp;
    longlong rnd;
    const type_conversion_status error =
        get_int(cs, from, len, &rnd, UINT_MAX24, INT_MIN24, INT_MAX24);
    store_tmp = unsigned_flag ? (int) (ulonglong) rnd : (int) rnd;
    int3store(ptr, store_tmp);
    return error;
}


type_conversion_status Field_medium::store(double nr)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    type_conversion_status error = TYPE_OK;
    nr = rint(nr);

    if (unsigned_flag) {
        if (nr < 0) {
            int3store(ptr, 0);
            set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
            error = TYPE_WARN_OUT_OF_RANGE;

        } else if (nr >= (double) (long) (1L << 24)) {
            uint32 tmp = (uint32) (1L << 24) - 1L;
            int3store(ptr, tmp);
            set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
            error = TYPE_WARN_OUT_OF_RANGE;

        } else
            int3store(ptr, (uint32) nr);

    } else {
        if (nr < (double) INT_MIN24) {
            long tmp = (long) INT_MIN24;
            int3store(ptr, tmp);
            set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
            error = TYPE_WARN_OUT_OF_RANGE;

        } else if (nr > (double) INT_MAX24) {
            long tmp = (long) INT_MAX24;
            int3store(ptr, tmp);
            set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
            error = TYPE_WARN_OUT_OF_RANGE;

        } else
            int3store(ptr, (long) nr);
    }

    return error;
}


type_conversion_status Field_medium::store(longlong nr, bool unsigned_val)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    type_conversion_status error = TYPE_OK;

    if (unsigned_flag) {
        if (nr < 0 && !unsigned_val) {
            int3store(ptr, 0);
            set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
            error = TYPE_WARN_OUT_OF_RANGE;

        } else if ((ulonglong) nr >= (ulonglong) (long) (1L << 24)) {
            long tmp = (long) (1L << 24) - 1L;
            int3store(ptr, tmp);
            set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
            error = TYPE_WARN_OUT_OF_RANGE;

        } else
            int3store(ptr, (uint32) nr);

    } else {
        if (nr < 0 && unsigned_val)
            nr = (ulonglong) (long) (1L << 24);       // Generate overflow

        if (nr < (longlong) INT_MIN24) {
            long tmp = (long) INT_MIN24;
            int3store(ptr, tmp);
            set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
            error = TYPE_WARN_OUT_OF_RANGE;

        } else if (nr > (longlong) INT_MAX24) {
            long tmp = (long) INT_MAX24;
            int3store(ptr, tmp);
            set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
            error = TYPE_WARN_OUT_OF_RANGE;

        } else
            int3store(ptr, (long) nr);
    }

    return error;
}


double Field_medium::val_real(void)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    long j = unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr);
    return (double) j;
}


longlong Field_medium::val_int(void)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    long j = unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr);
    return (longlong) j;
}


String *Field_medium::val_str(String *val_buffer,
                              String *val_ptr __attribute__((unused)))
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    const CHARSET_INFO *cs = &my_charset_numeric;
    uint length;
    uint mlength = max(field_length + 1, 10 * cs->mbmaxlen);
    val_buffer->alloc(mlength);
    char *to = (char *) val_buffer->ptr();
    long j = unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr);
    length = (uint) cs->cset->long10_to_str(cs, to, mlength, -10, j);
    val_buffer->length(length);

    if (zerofill)
        prepend_zeros(val_buffer); /* purecov: inspected */

    val_buffer->set_charset(cs);
    return val_buffer;
}


bool Field_medium::send_binary(Protocol *protocol)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    return protocol->store_long(Field_medium::val_int());
}


int Field_medium::cmp(const uchar *a_ptr, const uchar *b_ptr)
{
    long a, b;

    if (unsigned_flag) {
        a = uint3korr(a_ptr);
        b = uint3korr(b_ptr);

    } else {
        a = sint3korr(a_ptr);
        b = sint3korr(b_ptr);
    }

    return (a < b) ? -1 : (a > b) ? 1 : 0;
}

void Field_medium::make_sort_key(uchar *to, uint length)
{
    DBUG_ASSERT(length >= 3);

    if (unsigned_flag)
        to[0] = ptr[2];
    else
        to[0] = (uchar) (ptr[2] ^ 128);		/* Revers signbit */

    to[1] = ptr[1];
    to[2] = ptr[0];
}


void Field_medium::sql_type(String &res) const
{
    const CHARSET_INFO *cs = res.charset();
    res.length(cs->cset->snprintf(cs, (char *) res.ptr(), res.alloced_length(),
                                  "mediumint(%d)", (int) field_length));
    add_zerofill_and_unsigned(res);
}

/****************************************************************************
** long int
****************************************************************************/

type_conversion_status Field_long::store(const char *from, uint len,
        const CHARSET_INFO *cs)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    long store_tmp;
    longlong rnd;
    const type_conversion_status error =
        get_int(cs, from, len, &rnd, UINT_MAX32, INT_MIN32, INT_MAX32);
    store_tmp = unsigned_flag ? (long) (ulonglong) rnd : (long) rnd;
#ifdef WORDS_BIGENDIAN

    if (table->s->db_low_byte_first)
        int4store(ptr, store_tmp);

    else
#endif
        longstore(ptr, store_tmp);

    return error;
}


type_conversion_status Field_long::store(double nr)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    type_conversion_status error = TYPE_OK;
    int32 res;
    nr = rint(nr);

    if (unsigned_flag) {
        if (nr < 0) {
            res = 0;
            error = TYPE_WARN_OUT_OF_RANGE;

        } else if (nr > (double) UINT_MAX32) {
            res = UINT_MAX32;
            set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
            error = TYPE_WARN_OUT_OF_RANGE;

        } else
            res = (int32) (ulong) nr;

    } else {
        if (nr < (double) INT_MIN32) {
            res = (int32) INT_MIN32;
            error = TYPE_WARN_OUT_OF_RANGE;

        } else if (nr > (double) INT_MAX32) {
            res = (int32) INT_MAX32;
            error = TYPE_WARN_OUT_OF_RANGE;

        } else
            res = (int32) (longlong) nr;
    }

    if (error)
        set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);

#ifdef WORDS_BIGENDIAN

    if (table->s->db_low_byte_first)
        int4store(ptr, res);

    else
#endif
        longstore(ptr, res);

    return error;
}


/**
  Store a longlong in the field

  @param nr            the value to store
  @param unsigned_val  whether or not 'nr' should be interpreted as
                       signed or unsigned. E.g., if 'nr' has all bits
                       set it is interpreted as -1 if unsigned_val is
                       false and ULONGLONG_MAX if unsigned_val is true.
*/
type_conversion_status Field_long::store(longlong nr, bool unsigned_val)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    type_conversion_status error = TYPE_OK;
    int32 res;

    if (unsigned_flag) {
        if (nr < 0 && !unsigned_val) {
            res = 0;
            error = TYPE_WARN_OUT_OF_RANGE;

        } else if ((ulonglong) nr >= (LL(1) << 32)) {
            res = (int32) (uint32) ~0L;
            error = TYPE_WARN_OUT_OF_RANGE;

        } else
            res = (int32) (uint32) nr;

    } else {
        if (nr < 0 && unsigned_val) {
            nr = ((longlong) INT_MAX32) + 1;          // Generate overflow
            error = TYPE_WARN_OUT_OF_RANGE;
        }

        if (nr < (longlong) INT_MIN32) {
            res = (int32) INT_MIN32;
            error = TYPE_WARN_OUT_OF_RANGE;

        } else if (nr > (longlong) INT_MAX32) {
            res = (int32) INT_MAX32;
            error = TYPE_WARN_OUT_OF_RANGE;

        } else
            res = (int32) nr;
    }

    if (error)
        set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);

#ifdef WORDS_BIGENDIAN

    if (table->s->db_low_byte_first)
        int4store(ptr, res);

    else
#endif
        longstore(ptr, res);

    return error;
}


double Field_long::val_real(void)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    int32 j;
#ifdef WORDS_BIGENDIAN

    if (table->s->db_low_byte_first)
        j = sint4korr(ptr);
    else
#endif
        longget(j, ptr);

    return unsigned_flag ? (double) (uint32) j : (double) j;
}

longlong Field_long::val_int(void)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    int32 j;
    /* See the comment in Field_long::store(long long) */
    DBUG_ASSERT(table->in_use == current_thd);
#ifdef WORDS_BIGENDIAN

    if (table->s->db_low_byte_first)
        j = sint4korr(ptr);
    else
#endif
        longget(j, ptr);

    return unsigned_flag ? (longlong) (uint32) j : (longlong) j;
}

String *Field_long::val_str(String *val_buffer,
                            String *val_ptr __attribute__((unused)))
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    const CHARSET_INFO *cs = &my_charset_numeric;
    uint length;
    uint mlength = max(field_length + 1, 12 * cs->mbmaxlen);
    val_buffer->alloc(mlength);
    char *to = (char *) val_buffer->ptr();
    int32 j;
#ifdef WORDS_BIGENDIAN

    if (table->s->db_low_byte_first)
        j = sint4korr(ptr);
    else
#endif
        longget(j, ptr);

    if (unsigned_flag)
        length = cs->cset->long10_to_str(cs, to, mlength, 10, (long) (uint32)j);
    else
        length = cs->cset->long10_to_str(cs, to, mlength, -10, (long) j);

    val_buffer->length(length);

    if (zerofill)
        prepend_zeros(val_buffer);

    val_buffer->set_charset(cs);
    return val_buffer;
}


bool Field_long::send_binary(Protocol *protocol)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    return protocol->store_long(Field_long::val_int());
}

int Field_long::cmp(const uchar *a_ptr, const uchar *b_ptr)
{
    int32 a, b;
#ifdef WORDS_BIGENDIAN

    if (table->s->db_low_byte_first) {
        a = sint4korr(a_ptr);
        b = sint4korr(b_ptr);

    } else
#endif
    {
        longget(a, a_ptr);
        longget(b, b_ptr);
    }

    if (unsigned_flag)
        return ((uint32) a < (uint32) b) ? -1 : ((uint32) a > (uint32) b) ? 1 : 0;

    return (a < b) ? -1 : (a > b) ? 1 : 0;
}

void Field_long::make_sort_key(uchar *to, uint length)
{
    DBUG_ASSERT(length >= 4);
#ifdef WORDS_BIGENDIAN

    if (!table->s->db_low_byte_first) {
        if (unsigned_flag)
            to[0] = ptr[0];
        else
            to[0] = (char) (ptr[0] ^ 128);		/* Revers signbit */

        to[1]   = ptr[1];
        to[2]   = ptr[2];
        to[3]   = ptr[3];

    } else
#endif
    {
        if (unsigned_flag)
            to[0] = ptr[3];
        else
            to[0] = (char) (ptr[3] ^ 128);		/* Revers signbit */

        to[1]   = ptr[2];
        to[2]   = ptr[1];
        to[3]   = ptr[0];
    }
}


void Field_long::sql_type(String &res) const
{
    const CHARSET_INFO *cs = res.charset();
    res.length(cs->cset->snprintf(cs, (char *) res.ptr(), res.alloced_length(),
                                  "int(%d)", (int) field_length));
    add_zerofill_and_unsigned(res);
}

/****************************************************************************
 Field type longlong int (8 bytes)
****************************************************************************/

type_conversion_status Field_longlong::store(const char *from, uint len, const CHARSET_INFO *cs)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    int conv_err = 0;
    type_conversion_status error = TYPE_OK;
    char *end;
    ulonglong tmp;
    tmp = cs->cset->strntoull10rnd(cs, from, len, unsigned_flag, &end, &conv_err);

    if (conv_err == MY_ERRNO_ERANGE) {
        set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
        error = TYPE_WARN_OUT_OF_RANGE;

    } else if (table->in_use->count_cuted_fields &&
               check_int(cs, from, len, end, conv_err))
        error = TYPE_WARN_OUT_OF_RANGE;
    else
        error = TYPE_OK;

#ifdef WORDS_BIGENDIAN

    if (table->s->db_low_byte_first)
        int8store(ptr, tmp);

    else
#endif
        longlongstore(ptr, tmp);

    return error;
}


type_conversion_status Field_longlong::store(double nr)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    type_conversion_status error = TYPE_OK;
    longlong res;
    nr = rint(nr);

    if (unsigned_flag) {
        if (nr < 0) {
            res = 0;
            error = TYPE_WARN_OUT_OF_RANGE;

        } else if (nr >= (double) ULONGLONG_MAX) {
            res = ~(longlong) 0;
            error = TYPE_WARN_OUT_OF_RANGE;

        } else
            res = (longlong) double2ulonglong(nr);

    } else {
        if (nr <= (double) LONGLONG_MIN) {
            res = LONGLONG_MIN;

            if (nr < (double) LONGLONG_MIN)
                error = TYPE_WARN_OUT_OF_RANGE;

        } else if (nr >= (double) (ulonglong) LONGLONG_MAX) {
            res = LONGLONG_MAX;

            if (nr > (double) LONGLONG_MAX)
                error = TYPE_WARN_OUT_OF_RANGE;

        } else
            res = (longlong) nr;
    }

    if (error)
        set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);

#ifdef WORDS_BIGENDIAN

    if (table->s->db_low_byte_first)
        int8store(ptr, res);

    else
#endif
        longlongstore(ptr, res);

    return error;
}


type_conversion_status Field_longlong::store(longlong nr, bool unsigned_val)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    type_conversion_status error = TYPE_OK;

    if (nr < 0) {                                 // Only possible error
        /*
          if field is unsigned and value is signed (< 0) or
          if field is signed and value is unsigned we have an overflow
        */
        if (unsigned_flag != unsigned_val) {
            nr = unsigned_flag ? (ulonglong) 0 : (ulonglong) LONGLONG_MAX;
            set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
            error = TYPE_WARN_OUT_OF_RANGE;
        }
    }

#ifdef WORDS_BIGENDIAN

    if (table->s->db_low_byte_first)
        int8store(ptr, nr);

    else
#endif
        longlongstore(ptr, nr);

    return error;
}


double Field_longlong::val_real(void)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    longlong j;
#ifdef WORDS_BIGENDIAN

    if (table->s->db_low_byte_first)
        j = sint8korr(ptr);

    else
#endif
        longlongget(j, ptr);

    /* The following is open coded to avoid a bug in gcc 3.3 */
    if (unsigned_flag) {
        ulonglong tmp = (ulonglong) j;
        return ulonglong2double(tmp);
    }

    return (double) j;
}


longlong Field_longlong::val_int(void)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    longlong j;
#ifdef WORDS_BIGENDIAN

    if (table->s->db_low_byte_first)
        j = sint8korr(ptr);
    else
#endif
        longlongget(j, ptr);

    return j;
}


String *Field_longlong::val_str(String *val_buffer,
                                String *val_ptr __attribute__((unused)))
{
    const CHARSET_INFO *cs = &my_charset_numeric;
    uint length;
    uint mlength = max(field_length + 1, 22 * cs->mbmaxlen);
    val_buffer->alloc(mlength);
    char *to = (char *) val_buffer->ptr();
    longlong j;
#ifdef WORDS_BIGENDIAN

    if (table->s->db_low_byte_first)
        j = sint8korr(ptr);
    else
#endif
        longlongget(j, ptr);

    length = (uint) (cs->cset->longlong10_to_str)(cs, to, mlength,
             unsigned_flag ? 10 : -10, j);
    val_buffer->length(length);

    if (zerofill)
        prepend_zeros(val_buffer);

    val_buffer->set_charset(cs);
    return val_buffer;
}


bool Field_longlong::send_binary(Protocol *protocol)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    return protocol->store_longlong(Field_longlong::val_int(), unsigned_flag);
}


int Field_longlong::cmp(const uchar *a_ptr, const uchar *b_ptr)
{
    longlong a, b;
#ifdef WORDS_BIGENDIAN

    if (table->s->db_low_byte_first) {
        a = sint8korr(a_ptr);
        b = sint8korr(b_ptr);

    } else
#endif
    {
        longlongget(a, a_ptr);
        longlongget(b, b_ptr);
    }

    if (unsigned_flag)
        return ((ulonglong) a < (ulonglong) b) ? -1 :
               ((ulonglong) a > (ulonglong) b) ? 1 : 0;

    return (a < b) ? -1 : (a > b) ? 1 : 0;
}


void Field_longlong::make_sort_key(uchar *to, uint length)
{
    const int from_length = PACK_LENGTH;
    const int to_length = min<int>(from_length, length);
#ifdef WORDS_BIGENDIAN

    if (table == NULL || !table->s->db_low_byte_first)
        copy_integer<true>(to, to_length, ptr, from_length, unsigned_flag);
    else
#endif
        copy_integer<false>(to, to_length, ptr, from_length, unsigned_flag);
}


void Field_longlong::sql_type(String &res) const
{
    const CHARSET_INFO *cs = res.charset();
    res.length(cs->cset->snprintf(cs, (char *) res.ptr(), res.alloced_length(),
                                  "bigint(%d)", (int) field_length));
    add_zerofill_and_unsigned(res);
}


/*
  Floating-point numbers
 */

uchar *Field_real::pack(uchar *to, const uchar *from,
                        uint max_length, bool low_byte_first)
{
    DBUG_ENTER("Field_real::pack");
    DBUG_ASSERT(max_length >= pack_length());
#ifdef WORDS_BIGENDIAN

    if (low_byte_first != table->s->db_low_byte_first) {
        const uchar *dptr = from + pack_length();

        while (dptr-- > from)
            *to++ = *dptr;

        DBUG_RETURN(to);

    } else
#endif
        DBUG_RETURN(Field::pack(to, from, max_length, low_byte_first));
}

const uchar *Field_real::unpack(uchar *to, const uchar *from,
                                uint param_data, bool low_byte_first)
{
    DBUG_ENTER("Field_real::unpack");
#ifdef WORDS_BIGENDIAN

    if (low_byte_first != table->s->db_low_byte_first) {
        const uchar *dptr = from + pack_length();

        while (dptr-- > from)
            *to++ = *dptr;

        DBUG_RETURN(from + pack_length());

    } else
#endif
        DBUG_RETURN(Field::unpack(to, from, param_data, low_byte_first));
}


type_conversion_status Field_real::store_time(MYSQL_TIME *ltime,
        uint8 dec_arg __attribute__((unused)))
{
    double nr = TIME_to_double(ltime);
    return store(ltime->neg ? -nr : nr);
}


/****************************************************************************
  single precision float
****************************************************************************/

type_conversion_status Field_float::store(const char *from, uint len, const CHARSET_INFO *cs)
{
    int conv_error;
    type_conversion_status err = TYPE_OK;
    char *end;
    double nr = my_strntod(cs, (char *) from, len, &end, &conv_error);

    if (conv_error || (!len || ((uint) (end - from) != len &&
                                table->in_use->count_cuted_fields))) {
        set_warning(Sql_condition::WARN_LEVEL_WARN,
                    (conv_error ? ER_WARN_DATA_OUT_OF_RANGE
                     : WARN_DATA_TRUNCATED),
                    1);
        err = conv_error ? TYPE_WARN_OUT_OF_RANGE : TYPE_WARN_TRUNCATED;
    }

    Field_float::store(nr);
    return err;
}


type_conversion_status Field_float::store(double nr)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    const type_conversion_status error =
        truncate(&nr, FLT_MAX) ? TYPE_WARN_OUT_OF_RANGE : TYPE_OK;
    float j = (float)nr;
#ifdef WORDS_BIGENDIAN

    if (table->s->db_low_byte_first)
        float4store(ptr, j);

    else
#endif
        memcpy(ptr, &j, sizeof(j));

    return error;
}


type_conversion_status Field_float::store(longlong nr, bool unsigned_val)
{
    return Field_float::store(unsigned_val ? ulonglong2double((ulonglong) nr) :
                              (double) nr);
}


double Field_float::val_real(void)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    float j;
#ifdef WORDS_BIGENDIAN

    if (table->s->db_low_byte_first)
        float4get(j, ptr);

    else
#endif
        memcpy(&j, ptr, sizeof(j));

    return ((double) j);
}

longlong Field_float::val_int(void)
{
    float j;
#ifdef WORDS_BIGENDIAN

    if (table->s->db_low_byte_first)
        float4get(j, ptr);

    else
#endif
        memcpy(&j, ptr, sizeof(j));

    return (longlong) rint(j);
}


String *Field_float::val_str(String *val_buffer,
                             String *val_ptr __attribute__((unused)))
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    DBUG_ASSERT(!zerofill || field_length <= MAX_FIELD_CHARLENGTH);
    float nr;
#ifdef WORDS_BIGENDIAN

    if (table->s->db_low_byte_first)
        float4get(nr, ptr);

    else
#endif
        memcpy(&nr, ptr, sizeof(nr));

    uint to_length = 70;

    if (val_buffer->alloc(to_length)) {
        my_error(ER_OUT_OF_RESOURCES, MYF(0));
        return val_buffer;
    }

    char *to = (char *) val_buffer->ptr();
    size_t len;

    if (dec >= NOT_FIXED_DEC)
        len = my_gcvt(nr, MY_GCVT_ARG_FLOAT, to_length - 1, to, NULL);
    else {
        /*
          We are safe here because the buffer length is 70, and
          fabs(float) < 10^39, dec < NOT_FIXED_DEC. So the resulting string
          will be not longer than 69 chars + terminating '\0'.
        */
        len = my_fcvt(nr, dec, to, NULL);
    }

    val_buffer->length((uint) len);

    if (zerofill)
        prepend_zeros(val_buffer);

    val_buffer->set_charset(&my_charset_numeric);
    return val_buffer;
}


int Field_float::cmp(const uchar *a_ptr, const uchar *b_ptr)
{
    float a, b;
#ifdef WORDS_BIGENDIAN

    if (table->s->db_low_byte_first) {
        float4get(a, a_ptr);
        float4get(b, b_ptr);

    } else
#endif
    {
        memcpy(&a, a_ptr, sizeof(float));
        memcpy(&b, b_ptr, sizeof(float));
    }

    return (a < b) ? -1 : (a > b) ? 1 : 0;
}

#define FLT_EXP_DIG (sizeof(float)*8-FLT_MANT_DIG)

void Field_float::make_sort_key(uchar *to, uint length)
{
    DBUG_ASSERT(length >= 4);
    float nr;
#ifdef WORDS_BIGENDIAN

    if (table->s->db_low_byte_first)
        float4get(nr, ptr);

    else
#endif
        memcpy(&nr, ptr, min<uint>(length, sizeof(float)));

    uchar *tmp = to;

    if (nr == (float) 0.0) {
        /* Change to zero string */
        tmp[0] = (uchar) 128;
        memset(tmp + 1, 0, min<uint>(length, sizeof(nr) - 1));

    } else {
#ifdef WORDS_BIGENDIAN
        memcpy(tmp, &nr, sizeof(nr));
#else
        tmp[0] = ptr[3];
        tmp[1] = ptr[2];
        tmp[2] = ptr[1];
        tmp[3] = ptr[0];
#endif

        if (tmp[0] & 128) {			/* Negative */
            /* make complement */
            uint i;

            for (i = 0 ; i < sizeof(nr); i++)
                tmp[i] = (uchar) (tmp[i] ^ (uchar) 255);

        } else {
            ushort exp_part = (((ushort) tmp[0] << 8) | (ushort) tmp[1] |
                               (ushort) 32768);
            exp_part += (ushort) 1 << (16 - 1 - FLT_EXP_DIG);
            tmp[0] = (uchar) (exp_part >> 8);
            tmp[1] = (uchar) exp_part;
        }
    }
}


bool Field_float::send_binary(Protocol *protocol)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    return protocol->store((float) Field_float::val_real(), dec, (String *) 0);
}


/**
   Save the field metadata for float fields.

   Saves the pack length in the first byte.

   @param   metadata_ptr   First byte of field metadata

   @returns number of bytes written to metadata_ptr
*/
int Field_float::do_save_field_metadata(uchar *metadata_ptr)
{
    *metadata_ptr = pack_length();
    return 1;
}


void Field_float::sql_type(String &res) const
{
    if (dec == NOT_FIXED_DEC)
        res.set_ascii(STRING_WITH_LEN("float"));

    else {
        const CHARSET_INFO *cs = res.charset();
        res.length(cs->cset->snprintf(cs, (char *) res.ptr(), res.alloced_length(),
                                      "float(%d,%d)", (int) field_length, dec));
    }

    add_zerofill_and_unsigned(res);
}


/****************************************************************************
  double precision floating point numbers
****************************************************************************/

type_conversion_status Field_double::store(const char *from, uint len, const CHARSET_INFO *cs)
{
    int conv_error;
    type_conversion_status error = TYPE_OK;
    char *end;
    double nr = my_strntod(cs, (char *) from, len, &end, &conv_error);

    if ((conv_error != 0) || (!len || ((uint) (end - from) != len &&
                                       table->in_use->count_cuted_fields))) {
        set_warning(Sql_condition::WARN_LEVEL_WARN,
                    (conv_error ? ER_WARN_DATA_OUT_OF_RANGE
                     : WARN_DATA_TRUNCATED),
                    1);
        error = conv_error ? TYPE_WARN_OUT_OF_RANGE : TYPE_WARN_TRUNCATED;
    }

    Field_double::store(nr);
    return error;
}


type_conversion_status Field_double::store(double nr)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    const type_conversion_status error =
        truncate(&nr, DBL_MAX) ? TYPE_WARN_OUT_OF_RANGE : TYPE_OK;
#ifdef WORDS_BIGENDIAN

    if (table->s->db_low_byte_first)
        float8store(ptr, nr);

    else
#endif
        doublestore(ptr, nr);

    return error;
}


type_conversion_status Field_double::store(longlong nr, bool unsigned_val)
{
    return Field_double::store(unsigned_val ? ulonglong2double((ulonglong) nr) :
                               (double) nr);
}

/*
  If a field has fixed length, truncate the double argument pointed to by 'nr'
  appropriately.
  Also ensure that the argument is within [-max_value; max_value] range.
*/

bool Field_real::truncate(double *nr, double max_value)
{
    if (isnan(*nr)) {
        *nr = 0;
        set_null();
        set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
        return true;

    } else if (unsigned_flag && *nr < 0) {
        *nr = 0;
        set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
        return true;
    }

    if (!not_fixed) {
        uint order = field_length - dec;
        uint step = array_elements(log_10) - 1;
        max_value = 1.0;

        for (; order > step; order -= step)
            max_value *= log_10[step];

        max_value *= log_10[order];
        max_value -= 1.0 / log_10[dec];

        /* Check for infinity so we don't get NaN in calculations */
        if (!my_isinf(*nr)) {
            double tmp = rint((*nr - floor(*nr)) * log_10[dec]) / log_10[dec];
            *nr = floor(*nr) + tmp;
        }
    }

    if (*nr < -max_value) {
        *nr = -max_value;
        set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
        return true;

    } else if (*nr > max_value) {
        *nr = max_value;
        set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
        return true;
    }

    return false;
}


type_conversion_status Field_real::store_decimal(const my_decimal *dm)
{
    double dbl;
    my_decimal2double(E_DEC_FATAL_ERROR, dm, &dbl);
    return store(dbl);
}

double Field_double::val_real(void)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    double j;
#ifdef WORDS_BIGENDIAN

    if (table->s->db_low_byte_first)
        float8get(j, ptr);

    else
#endif
        doubleget(j, ptr);

    return j;
}

longlong Field_double::val_int(void)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    double j;
    longlong res;
#ifdef WORDS_BIGENDIAN

    if (table->s->db_low_byte_first)
        float8get(j, ptr);

    else
#endif
        doubleget(j, ptr);

    /* Check whether we fit into longlong range */
    if (j <= (double) LONGLONG_MIN) {
        res = (longlong) LONGLONG_MIN;
        goto warn;
    }

    if (j >= (double) (ulonglong) LONGLONG_MAX) {
        res = (longlong) LONGLONG_MAX;
        goto warn;
    }

    return (longlong) rint(j);
warn: {
        char buf[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE];
        String tmp(buf, sizeof(buf), &my_charset_latin1), *str;
        str = val_str(&tmp, 0);
        ErrConvString err(str);
        push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
                            ER_TRUNCATED_WRONG_VALUE,
                            ER(ER_TRUNCATED_WRONG_VALUE), "INTEGER",
                            err.ptr());
    }
    return res;
}


my_decimal *Field_real::val_decimal(my_decimal *decimal_value)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    double2my_decimal(E_DEC_FATAL_ERROR, val_real(), decimal_value);
    return decimal_value;
}


bool Field_real::get_date(MYSQL_TIME *ltime, uint fuzzydate)
{
    return my_double_to_datetime_with_warn(val_real(), ltime, fuzzydate);
}


bool Field_real::get_time(MYSQL_TIME *ltime)
{
    return my_double_to_time_with_warn(val_real(), ltime);
}


String *Field_double::val_str(String *val_buffer,
                              String *val_ptr __attribute__((unused)))
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    DBUG_ASSERT(!zerofill || field_length <= MAX_FIELD_CHARLENGTH);
    double nr;
#ifdef WORDS_BIGENDIAN

    if (table->s->db_low_byte_first)
        float8get(nr, ptr);

    else
#endif
        doubleget(nr, ptr);

    uint to_length = DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE;

    if (val_buffer->alloc(to_length)) {
        my_error(ER_OUT_OF_RESOURCES, MYF(0));
        return val_buffer;
    }

    char *to = (char *) val_buffer->ptr();
    size_t len;

    if (dec >= NOT_FIXED_DEC)
        len = my_gcvt(nr, MY_GCVT_ARG_DOUBLE, to_length - 1, to, NULL);
    else
        len = my_fcvt(nr, dec, to, NULL);

    val_buffer->length((uint) len);

    if (zerofill)
        prepend_zeros(val_buffer);

    val_buffer->set_charset(&my_charset_numeric);
    return val_buffer;
}

bool Field_double::send_binary(Protocol *protocol)
{
    return protocol->store((double) Field_double::val_real(), dec, (String *) 0);
}


int Field_double::cmp(const uchar *a_ptr, const uchar *b_ptr)
{
    double a, b;
#ifdef WORDS_BIGENDIAN

    if (table->s->db_low_byte_first) {
        float8get(a, a_ptr);
        float8get(b, b_ptr);

    } else
#endif
    {
        doubleget(a, a_ptr);
        doubleget(b, b_ptr);
    }

    return (a < b) ? -1 : (a > b) ? 1 : 0;
}


#define DBL_EXP_DIG (sizeof(double)*8-DBL_MANT_DIG)

/* The following should work for IEEE */

void Field_double::make_sort_key(uchar *to, uint length)
{
    double nr;
#ifdef WORDS_BIGENDIAN

    if (table->s->db_low_byte_first)
        float8get(nr, ptr);

    else
#endif
        doubleget(nr, ptr);

    if (length < 8) {
        uchar buff[8];
        change_double_for_sort(nr, buff);
        memcpy(to, buff, length);

    } else
        change_double_for_sort(nr, to);
}


/**
   Save the field metadata for double fields.

   Saves the pack length in the first byte of the field metadata array
   at index of *metadata_ptr.

   @param   metadata_ptr   First byte of field metadata

   @returns number of bytes written to metadata_ptr
*/
int Field_double::do_save_field_metadata(uchar *metadata_ptr)
{
    *metadata_ptr = pack_length();
    return 1;
}


void Field_double::sql_type(String &res) const
{
    const CHARSET_INFO *cs = res.charset();

    if (dec == NOT_FIXED_DEC)
        res.set_ascii(STRING_WITH_LEN("double"));

    else {
        res.length(cs->cset->snprintf(cs, (char *) res.ptr(), res.alloced_length(),
                                      "double(%d,%d)", (int) field_length, dec));
    }

    add_zerofill_and_unsigned(res);
}


/****************************************************************************
** Common code for all temporal data types: DATE, DATETIME, TIMESTAMP, TIME
*****************************************************************************/

uint Field_temporal::is_equal(Create_field *new_field)
{
    return new_field->sql_type == real_type() &&
           new_field->decimals == decimals();
}


my_decimal *Field_temporal::val_decimal(my_decimal *decimal_value)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    DBUG_ASSERT(decimals() == 0);
    int2my_decimal(E_DEC_FATAL_ERROR, val_int(), 0, decimal_value);
    return decimal_value;
}


/**
  Set warnings from a warning vector.
  Note, multiple warnings can be set at the same time.

  @param str       Value.
  @param warnings  Warning vector.
*/
void Field_temporal::set_warnings(ErrConvString str, int warnings)
{
    int cut_incremented = 0;
    timestamp_type ts_type = field_type_to_timestamp_type(type());

    if (warnings & MYSQL_TIME_WARN_TRUNCATED) {
        set_datetime_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED,
                             str, ts_type, !cut_incremented);
        cut_incremented = 1;
    }

    if (warnings & (MYSQL_TIME_WARN_OUT_OF_RANGE | MYSQL_TIME_WARN_ZERO_DATE |
                    MYSQL_TIME_WARN_ZERO_IN_DATE)) {
        set_datetime_warning(Sql_condition::WARN_LEVEL_WARN,
                             ER_WARN_DATA_OUT_OF_RANGE,
                             str, ts_type, !cut_incremented);
        cut_incremented = 1;
    }

    if (warnings & MYSQL_TIME_WARN_INVALID_TIMESTAMP) {
        set_datetime_warning(Sql_condition::WARN_LEVEL_WARN,
                             ER_WARN_INVALID_TIMESTAMP,
                             str, ts_type, !cut_incremented);
        cut_incremented = 1;
    }

    if ((warnings & MYSQL_TIME_NOTE_TRUNCATED) &&
            !(warnings & MYSQL_TIME_WARN_TRUNCATED)) {
        set_datetime_warning(Sql_condition::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED,
                             str, ts_type, !cut_incremented);
    }
}


type_conversion_status Field_temporal::store(longlong nr, bool unsigned_val)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    int warnings = 0;
    MYSQL_TIME ltime;
    type_conversion_status error = convert_number_to_TIME(nr, unsigned_val, 0,
                                   &ltime, &warnings);

    if (error == TYPE_OK || error == TYPE_NOTE_TRUNCATED)
        error = store_internal(&ltime, &warnings);
    else {
        DBUG_ASSERT(warnings != 0); // Must be set by convert_number_to_TIME

        if (warnings & (MYSQL_TIME_WARN_ZERO_DATE |
                        MYSQL_TIME_WARN_ZERO_IN_DATE) &&
                !current_thd->is_strict_mode())
            error = TYPE_NOTE_TIME_TRUNCATED;
    }

    if (warnings)
        set_warnings(ErrConvString(nr, unsigned_val), warnings);

    return error;
}


type_conversion_status Field_temporal::store_lldiv_t(const lldiv_t *lld, int *warnings)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    type_conversion_status error;
    MYSQL_TIME ltime;
    error = convert_number_to_TIME(lld->quot, 0, lld->rem, &ltime, warnings);

    if (error == TYPE_OK || error == TYPE_NOTE_TRUNCATED)
        error = store_internal_with_round(&ltime, warnings);
    else if (!*warnings) {
        DBUG_ASSERT(warnings != 0); // Must be set by convert_number_to_TIME

        if (((*warnings & MYSQL_TIME_WARN_ZERO_DATE) != 0 ||
                (*warnings & MYSQL_TIME_WARN_ZERO_IN_DATE) != 0) &&
                !current_thd->is_strict_mode())
            error = TYPE_NOTE_TIME_TRUNCATED;
    }

    return error;
}


type_conversion_status Field_temporal::store_decimal(const my_decimal *decimal)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    lldiv_t lld;
    int warnings = 0;
    /* Pass 0 in the first argument, not to produce warnings automatically */
    my_decimal2lldiv_t(0, decimal, &lld);
    const type_conversion_status error = store_lldiv_t(&lld, &warnings);

    if (warnings)
        set_warnings(ErrConvString(decimal), warnings);

    return error;
}


type_conversion_status Field_temporal::store(double nr)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    int warnings = 0;
    lldiv_t lld;
    double2lldiv_t(nr, &lld);
    const type_conversion_status error = store_lldiv_t(&lld, &warnings);

    if (warnings)
        set_warnings(ErrConvString(nr), warnings);

    return error;
}


/**
  Store string into a date/time/datetime field.

  @param from     Date/time string
  @param  len     Length of the string
  @param  cs      Character set of the string

  @retval TYPE_OK   Storage of value went fine without warnings or errors
  @retval !TYPE_OK  Warning/error as indicated by type_conversion_status enum
                    value
*/
type_conversion_status Field_temporal::store(const char *str, uint len, const CHARSET_INFO *cs)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    type_conversion_status error = TYPE_OK;
    MYSQL_TIME ltime;
    MYSQL_TIME_STATUS status;

    if (convert_str_to_TIME(str, len, cs, &ltime, &status)) {
        /*
          When convert_str_to_TIME() returns error, ltime has been set to
          0 so there's nothing to store in the field.
        */
        reset();

        if (status.warnings & (MYSQL_TIME_WARN_ZERO_DATE |
                               MYSQL_TIME_WARN_ZERO_IN_DATE) &&
                !current_thd->is_strict_mode())
            error = TYPE_NOTE_TIME_TRUNCATED;
        else
            error = TYPE_ERR_BAD_VALUE;

    } else {
        error = time_warning_to_type_conversion_status(status.warnings);
        const type_conversion_status tmp_error = store_internal_with_round(&ltime,
                &status.warnings);

        // Return the most serious error of the two, see type_conversion_status
        if (tmp_error > error)
            error = tmp_error;
    }

    if (status.warnings)
        set_warnings(ErrConvString(str, len, cs), status.warnings);

    return error;
}


/**
  @retval -1              Timestamp with wrong values
  @retval anything else   DATETIME as integer in YYYYMMDDHHMMSS format
*/
longlong Field_temporal::convert_number_to_datetime(longlong nr, bool unsigned_val,
        MYSQL_TIME *ltime, int *warnings)
{
    /*
      Note, number_to_datetime can return a result different from nr:
      e.g. 111111 -> 20111111000000
    */
    longlong tmp = number_to_datetime(nr, ltime, date_flags(), warnings);

    if (tmp == LL(-1))
        reset();

    return tmp;
}


/****************************************************************************
** Common code for temporal data types with date: DATE, DATETIME, TIMESTAMP
*****************************************************************************/


bool Field_temporal_with_date::get_internal_check_zero(MYSQL_TIME *ltime,
        uint fuzzydate)
{
    if (get_date_internal(ltime)) { /* '0000-00-00' */
        DBUG_ASSERT(type() == MYSQL_TYPE_TIMESTAMP);

        if (fuzzydate & TIME_NO_ZERO_DATE)
            return true;

        set_zero_time(ltime, MYSQL_TIMESTAMP_DATETIME);
    }

    return false;
}


longlong Field_temporal_with_date::val_date_temporal()
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    MYSQL_TIME ltime;
    return get_date_internal(&ltime) ?
           0 : TIME_to_longlong_datetime_packed(&ltime);
}


longlong Field_temporal_with_date::val_time_temporal()
{
    /*
      There are currently no tests covering this method,
      as DATETIME seems to always superseed over TIME in comparison.
    */
    ASSERT_COLUMN_MARKED_FOR_READ;
    MYSQL_TIME ltime;
    return get_date_internal(&ltime) ? 0 : TIME_to_longlong_time_packed(&ltime);
}


/**
  Convert a number in format YYMMDDhhmmss to string.
  Straight coded to avoid problem with slow longlong arithmetic and sprintf.

  @param OUT  String - pointer to convert to.
  @param      tmp    - number with datetime value.
*/
static inline int my_datetime_number_to_str(char *pos, longlong tmp)
{
    long part1 = (long) (tmp / 1000000LL);
    long part2 = (long) (tmp - (ulonglong) part1 * 1000000LL);
    int part3;
    pos += MAX_DATETIME_WIDTH; /* Start from the end */
    *pos-- = 0;
    *pos-- = (char) ('0' + (char) (part2 % 10)); /* Seconds */
    part2 /= 10;
    *pos-- = (char) ('0' + (char) (part2 % 10));
    part3 = (int) (part2 / 10);
    *pos-- = ':';
    *pos-- = (char) ('0' + (char) (part3 % 10)); /* Minutes */
    part3 /= 10;
    *pos-- = (char) ('0' + (char) (part3 % 10));
    part3 /= 10;
    *pos-- = ':';
    *pos-- = (char) ('0' + (char) (part3 % 10)); /* Hours */
    part3 /= 10;
    *pos-- = (char) ('0' + (char) part3);
    *pos-- = ' ';
    *pos-- = (char) ('0' + (char) (part1 % 10)); /* Day */
    part1 /= 10;
    *pos-- = (char) ('0' + (char) (part1 % 10));
    part1 /= 10;
    *pos-- = '-';
    *pos-- = (char) ('0' + (char) (part1 % 10)); /* Month */
    part1 /= 10;
    *pos-- = (char) ('0' + (char) (part1 % 10));
    part3 = (int) (part1 / 10);
    *pos-- = '-';
    *pos-- = (char) ('0' + (char) (part3 % 10)); /* Year */
    part3 /= 10;
    *pos-- = (char) ('0' + (char) (part3 % 10));
    part3 /= 10;
    *pos-- = (char) ('0' + (char) (part3 % 10));
    part3 /= 10;
    *pos = (char) ('0' + (char) part3);
    return MAX_DATETIME_WIDTH;
}


String *Field_temporal_with_date::val_str(String *val_buffer, String *val_ptr)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    MYSQL_TIME ltime;
    val_buffer->alloc(field_length + 1);
    val_buffer->set_charset(&my_charset_numeric);

    if (get_date_internal(&ltime)) {
        val_buffer->set_ascii(my_zero_datetime6, field_length);
        return val_buffer;
    }

    make_datetime((DATE_TIME_FORMAT *) 0, &ltime, val_buffer, dec);
    return val_buffer;
}


type_conversion_status Field_temporal_with_date::convert_number_to_TIME(longlong nr,
        bool unsigned_val,
        int nanoseconds,
        MYSQL_TIME *ltime,
        int *warnings)
{
    if (nr < 0 || nanoseconds < 0) {
        reset();
        *warnings |= MYSQL_TIME_WARN_OUT_OF_RANGE;
        return TYPE_WARN_OUT_OF_RANGE;
    }

    if (convert_number_to_datetime(nr, unsigned_val, ltime, warnings) == LL(-1))
        return TYPE_ERR_BAD_VALUE;

    if (ltime->time_type == MYSQL_TIMESTAMP_DATE && nanoseconds) {
        *warnings |= MYSQL_TIME_WARN_TRUNCATED;
        return TYPE_NOTE_TRUNCATED;
    }

    ltime->second_part = 0;

    if (datetime_add_nanoseconds_with_round(ltime, nanoseconds, warnings)) {
        reset();
        return TYPE_WARN_OUT_OF_RANGE;
    }

    return TYPE_OK;
}


type_conversion_status Field_temporal_with_date::store_time(MYSQL_TIME *ltime,
        uint8 dec_arg __attribute__((unused)))
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    type_conversion_status error;
    int warnings = 0;

    switch (ltime->time_type) { // TS-TODO: split into separate methods?
    case MYSQL_TIMESTAMP_DATETIME:
    case MYSQL_TIMESTAMP_DATE:
        if (check_date(ltime, non_zero_date(ltime), date_flags(), &warnings)) {
            DBUG_ASSERT(warnings &
                        (MYSQL_TIME_WARN_OUT_OF_RANGE |
                         MYSQL_TIME_WARN_ZERO_DATE |
                         MYSQL_TIME_WARN_ZERO_IN_DATE));
            error = time_warning_to_type_conversion_status(warnings);
            reset();

        } else
            error = store_internal_with_round(ltime, &warnings);

        break;

    case MYSQL_TIMESTAMP_TIME: {
        /* Convert TIME to DATETIME */
        THD *thd = table ? table->in_use : current_thd;
        MYSQL_TIME ltime2;
        time_to_datetime(thd, ltime, &ltime2);
        error = store_internal_with_round(&ltime2, &warnings);
        break;
    }

    case MYSQL_TIMESTAMP_NONE:
    case  MYSQL_TIMESTAMP_ERROR:
    default:
        warnings |= MYSQL_TIME_WARN_TRUNCATED;
        reset();
        error = TYPE_WARN_TRUNCATED;
    }

    if (warnings)
        set_warnings(ErrConvString(ltime, decimals()), warnings);

    return error;
}


bool Field_temporal_with_date::convert_str_to_TIME(const char *str, uint len,
        const CHARSET_INFO *cs,
        MYSQL_TIME *ltime,
        MYSQL_TIME_STATUS *status)
{
    return str_to_datetime(cs, str, len, ltime, date_flags(), status);
}


bool Field_temporal_with_date::send_binary(Protocol *protocol)
{
    MYSQL_TIME ltime;

    if (get_date_internal(&ltime)) {
        // Only MYSQL_TYPE_TIMESTAMP can return an error in get_date_internal()
        DBUG_ASSERT(type() == MYSQL_TYPE_TIMESTAMP);
        set_zero_time(&ltime, MYSQL_TIMESTAMP_DATETIME);
    }

    return protocol->store(&ltime, 0);
}


type_conversion_status Field_temporal_with_date::store_internal_with_round(MYSQL_TIME *ltime,
        int *warnings)
{
    if (my_datetime_round(ltime, dec, warnings)) {
        reset();
        return time_warning_to_type_conversion_status(*warnings);

    } else
        return store_internal(ltime, warnings);
}


/****************************************************************************
** Common code for data types with date and time: DATETIME, TIMESTAMP
*****************************************************************************/


void Field_temporal_with_date_and_time::store_timestamp(const struct timeval *tm)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;

    if (!my_time_fraction_remainder(tm->tv_usec, decimals())) {
        store_timestamp_internal(tm);
        return;
    }

    struct timeval tm2 = *tm;

    my_timeval_round(&tm2, decimals());

    store_timestamp_internal(&tm2);
}


bool Field_temporal_with_date_and_time::convert_TIME_to_timestamp(THD *thd,
        const MYSQL_TIME *ltime,
        struct timeval *tm,
        int *warnings)
{
    /*
      No needs to do check_date(TIME_NO_ZERO_IN_DATE),
      because it has been done earlier in
      store_time(), number_to_datetime() or str_to_datetime().
    */
    if (datetime_with_no_zero_in_date_to_timeval(thd, ltime, tm, warnings)) {
        tm->tv_sec = tm->tv_usec = 0;
        return true;
    }

    return false;
}


void Field_temporal_with_date_and_time::init_timestamp_flags()
{
    if (unireg_check != NONE) {
        /*
          This TIMESTAMP column is hereby quietly assumed to have an insert or
          update default function.
        */
        flags |= TIMESTAMP_FLAG;

        if (unireg_check != TIMESTAMP_DN_FIELD)
            flags |= ON_UPDATE_NOW_FLAG;
    }
}

/****************************************************************************
** Common code for DATETIME(N) and TIMESTAMP(N)
*****************************************************************************/

double Field_temporal_with_date_and_timef::val_real()
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    MYSQL_TIME ltime;
    return get_date_internal(&ltime) ? 0 : TIME_to_double_datetime(&ltime);
}


longlong Field_temporal_with_date_and_timef::val_int()
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    MYSQL_TIME ltime;
    return get_date_internal(&ltime) ?
           0 : TIME_to_ulonglong_datetime_round(&ltime);
}


my_decimal *Field_temporal_with_date_and_timef::val_decimal(my_decimal *dec)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    MYSQL_TIME ltime;

    if (get_date_internal(&ltime)) {
        // Only MYSQL_TYPE_TIMESTAMP can return an error in get_date_internal()
        DBUG_ASSERT(type() == MYSQL_TYPE_TIMESTAMP);
        set_zero_time(&ltime, MYSQL_TIMESTAMP_DATETIME);
    }

    return date2my_decimal(&ltime, dec);
}


/**
  TIMESTAMP type columns hold date and time values in the range 1970-01-01
  00:00:01 UTC to 2038-01-01 00:00:00 UTC, stored as number of seconds since
  the start of the Unix Epoch (1970-01-01 00:00:01 UTC.)

  TIMESTAMP columns can be automatically set on row updates to and/or have
  CURRENT_TIMESTAMP as default value for inserts.

  The implementation of function defaults is heavily entangled with the binary
  .frm file format. The @c utype @c enum is part of the file format
  specification but is declared a member of the Field class. This constructor
  accepts a unireg_check value to initialize the column default expression.

  Five distinct unireg_check values are used for TIMESTAMP columns to
  distinguish various cases of DEFAULT or ON UPDATE values. These values are:

  - TIMESTAMP_OLD_FIELD - old timestamp, this has no significance when
    creating a the Field_timestamp.

  - TIMESTAMP_DN_FIELD - means TIMESTAMP DEFAULT CURRENT_TIMESTAMP.

  - TIMESTAMP_UN_FIELD - means TIMESTAMP DEFAULT <default value> ON UPDATE
    CURRENT_TIMESTAMP, where <default value> is an implicit or explicit
    expression other than CURRENT_TIMESTAMP or any synonym thereof
    (e.g. NOW().)

  - TIMESTAMP_DNUN_FIELD - means DEFAULT CURRENT_TIMESTAMP ON UPDATE
    CURRENT_TIMESTAMP.

  - NONE - means that the column has neither DEFAULT CURRENT_TIMESTAMP, nor ON
    UPDATE CURRENT_TIMESTAMP

  Note that columns with TIMESTAMP_OLD_FIELD are no longer created explicitly,
  the value is meant to preserve the ability to read tables from old
  databases. Such columns are replaced with their newer counterparts by CREATE
  TABLE and SHOW CREATE TABLE. This is because we want to prefer NONE
  unireg_check over TIMESTAMP_OLD_FIELD for "TIMESTAMP DEFAULT 'Const'"
  field. (Old TIMESTAMP columns allowed such definitions as well but ignored
  the default value for first the TIMESTAMP column. This is, of course,
  non-standard.)  In most cases a user won't notice any change, only exception
  being different behavior of old/new TIMESTAMPS columns during ALTER TABLE.
 */
Field_timestamp::Field_timestamp(uchar *ptr_arg, uint32 len_arg,
                                 uchar *null_ptr_arg, uchar null_bit_arg,
                                 enum utype unireg_check_arg,
                                 const char *field_name_arg)
    : Field_temporal_with_date_and_time(ptr_arg, null_ptr_arg, null_bit_arg,
                                        unireg_check_arg, field_name_arg, 0)
{
    init_timestamp_flags();
    /* For 4.0 MYD and 4.0 InnoDB compatibility */
    flags |= ZEROFILL_FLAG | UNSIGNED_FLAG;
}


Field_timestamp::Field_timestamp(bool maybe_null_arg,
                                 const char *field_name_arg)
    : Field_temporal_with_date_and_time((uchar *) 0,
                                        maybe_null_arg ? (uchar *) "" : 0, 0,
                                        NONE, field_name_arg, 0)
{
    init_timestamp_flags();
    /* For 4.0 MYD and 4.0 InnoDB compatibility */
    flags |= ZEROFILL_FLAG | UNSIGNED_FLAG;
}


ulonglong Field_timestamp::date_flags(const THD *thd)
{
    /* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */
    return (thd->variables.sql_mode & MODE_NO_ZERO_DATE) | MODE_NO_ZERO_IN_DATE;
}


type_conversion_status Field_timestamp::store_internal(const MYSQL_TIME *ltime, int *warnings)
{
    THD *thd = table ? table->in_use : current_thd;
    struct timeval tm;
    convert_TIME_to_timestamp(thd, ltime, &tm, warnings);
    const type_conversion_status error =
        time_warning_to_type_conversion_status(*warnings);
    store_timestamp_internal(&tm);
    return error;
}


/**
  Get a value from record, without checking fuzzy date flags.
  @retval true  - if timestamp is 0, ltime is not touched in this case.
  @retval false - if timestamp is non-zero.
*/
bool Field_timestamp::get_date_internal(MYSQL_TIME *ltime)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    uint32 temp;
    THD  *thd = table ? table->in_use : current_thd;
#ifdef WORDS_BIGENDIAN

    if (table && table->s->db_low_byte_first)
        temp = uint4korr(ptr);
    else
#endif
        longget(temp, ptr);

    if (!temp)
        return true;

    thd->time_zone()->gmt_sec_to_TIME(ltime, (my_time_t) temp);
    return false;
}


/**
   Get TIMESTAMP field value as seconds since begging of Unix Epoch
*/
bool Field_timestamp::get_timestamp(struct timeval *tm, int *warnings)
{
    if (is_null())
        return true;

    tm->tv_usec = 0;
#ifdef WORDS_BIGENDIAN

    if (table && table->s->db_low_byte_first) {
        tm->tv_sec = sint4korr(ptr);
        return false;
    }

#endif
    long tmp;
    longget(tmp, ptr);
    tm->tv_sec = tmp;
    return false;
}


void Field_timestamp::store_timestamp_internal(const struct timeval *tm)
{
#ifdef WORDS_BIGENDIAN

    if (table && table->s->db_low_byte_first)
        int4store(ptr, tm->tv_sec);

    else
#endif
        longstore(ptr, (uint32) tm->tv_sec);
}


type_conversion_status Field_timestamp::store_packed(longlong nr)
{
    /* Make sure the stored value was previously properly rounded or truncated */
    DBUG_ASSERT((MY_PACKED_TIME_GET_FRAC_PART(nr) %
                 (int) log_10_int[DATETIME_MAX_DECIMALS - decimals()]) == 0);
    MYSQL_TIME ltime;
    TIME_from_longlong_datetime_packed(&ltime, nr);
    return Field_timestamp::store_time(&ltime, 0);
}


longlong Field_timestamp::val_int()
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    MYSQL_TIME ltime;
    return get_date_internal(&ltime) ? 0 : TIME_to_ulonglong_datetime(&ltime);
}


bool Field_timestamp::get_date(MYSQL_TIME *ltime, uint fuzzydate)
{
    /* Don't do check_fuzzy_date() as month and year are never 0 for timestamp */
    return get_internal_check_zero(ltime, fuzzydate);
}


int Field_timestamp::cmp(const uchar *a_ptr, const uchar *b_ptr)
{
    int32 a, b;
#ifdef WORDS_BIGENDIAN

    if (table && table->s->db_low_byte_first) {
        a = sint4korr(a_ptr);
        b = sint4korr(b_ptr);

    } else
#endif
    {
        longget(a, a_ptr);
        longget(b, b_ptr);
    }

    return ((uint32) a < (uint32) b) ? -1 : ((uint32) a > (uint32) b) ? 1 : 0;
}


void Field_timestamp::make_sort_key(uchar *to, uint length __attribute__((unused)))
{
#ifdef WORDS_BIGENDIAN

    if (!table || !table->s->db_low_byte_first) {
        to[0] = ptr[0];
        to[1] = ptr[1];
        to[2] = ptr[2];
        to[3] = ptr[3];

    } else
#endif
    {
        to[0] = ptr[3];
        to[1] = ptr[2];
        to[2] = ptr[1];
        to[3] = ptr[0];
    }
}


void Field_timestamp::sql_type(String &res) const
{
    res.set_ascii(STRING_WITH_LEN("timestamp"));
}


/****************************************************************************
** timestamp(N) type
** In string context: YYYY-MM-DD HH:MM:SS.FFFFFF
** In number context: YYYYMMDDHHMMSS.FFFFFF
** Stored as a 7 byte value
****************************************************************************/
Field_timestampf::Field_timestampf(uchar *ptr_arg,
                                   uchar *null_ptr_arg, uchar null_bit_arg,
                                   enum utype unireg_check_arg,
                                   const char *field_name_arg,
                                   uint8 dec_arg)
    : Field_temporal_with_date_and_timef(ptr_arg, null_ptr_arg, null_bit_arg,
                                         unireg_check_arg, field_name_arg,
                                         dec_arg)
{
    init_timestamp_flags();
}


Field_timestampf::Field_timestampf(bool maybe_null_arg,
                                   const char *field_name_arg,
                                   uint8 dec_arg)
    : Field_temporal_with_date_and_timef((uchar *) 0,
                                         maybe_null_arg ? (uchar *) "" : 0, 0,
                                         NONE, field_name_arg, dec_arg)
{
    if (unireg_check != TIMESTAMP_DN_FIELD)
        flags |= ON_UPDATE_NOW_FLAG;
}


ulonglong Field_timestampf::date_flags(const THD *thd)
{
    /* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */
    return (thd->variables.sql_mode & MODE_NO_ZERO_DATE) | MODE_NO_ZERO_IN_DATE;
}


type_conversion_status Field_timestampf::reset()
{
    memset(ptr, 0, pack_length());
    return TYPE_OK;
}


void Field_timestampf::store_timestamp_internal(const struct timeval *tm)
{
    my_timestamp_to_binary(tm, ptr, dec);
}


type_conversion_status Field_timestampf::store_internal(const MYSQL_TIME *ltime, int *warnings)
{
    THD *thd = table ? table->in_use : current_thd;
    struct timeval tm;
    convert_TIME_to_timestamp(thd, ltime, &tm, warnings);
    const type_conversion_status error =
        time_warning_to_type_conversion_status(*warnings);
    store_timestamp_internal(&tm);
    return error;
}


type_conversion_status Field_timestampf::store_packed(longlong nr)
{
    MYSQL_TIME ltime;
    TIME_from_longlong_datetime_packed(&ltime, nr);
    return Field_timestampf::store_time(&ltime, dec);
}


bool Field_timestampf::get_date(MYSQL_TIME *ltime, uint fuzzydate)
{
    /* Don't do check_fuzzy_date() as month and year are never 0 for timestamp */
    return get_internal_check_zero(ltime, fuzzydate);
}


void Field_timestampf::sql_type(String &res) const
{
    if (dec == 0) {
        res.set_ascii(STRING_WITH_LEN("timestamp"));
        return;
    }

    const CHARSET_INFO *cs = res.charset();
    res.length(cs->cset->snprintf(cs, (char *) res.ptr(), res.alloced_length(),
                                  "timestamp(%d)", dec));
}


bool Field_timestampf::get_date_internal(MYSQL_TIME *ltime)
{
    THD *thd = table ? table->in_use : current_thd;
    struct timeval tm;
    my_timestamp_from_binary(&tm, ptr, dec);

    if (tm.tv_sec == 0)
        return true;

    thd->time_zone()->gmt_sec_to_TIME(ltime, tm);
    return false;
}


bool Field_timestampf::get_timestamp(struct timeval *tm, int *warnings)
{
    THD *thd = table ? table->in_use : current_thd;
    thd->time_zone_used = 1;
    DBUG_ASSERT(!is_null());
    my_timestamp_from_binary(tm, ptr, dec);
    return false;
}


/****************************************************************************
** TIME and TIME(N) common methods
****************************************************************************/


bool Field_time_common::convert_str_to_TIME(const char *str, uint len,
        const CHARSET_INFO *cs,
        MYSQL_TIME *ltime,
        MYSQL_TIME_STATUS *status)
{
    return str_to_time(cs, str, len, ltime, 0, status);
}


type_conversion_status Field_time_common::convert_number_to_TIME(longlong nr, bool unsigned_val,
        int nanoseconds,
        MYSQL_TIME *ltime, int *warnings)
{
    if (unsigned_val && nr < 0) {
        *warnings |= MYSQL_TIME_WARN_OUT_OF_RANGE;
        set_max_time(ltime, 0);
        store_internal(ltime, warnings);
        return TYPE_WARN_OUT_OF_RANGE;
    }

    if (number_to_time(nr, ltime, warnings)) {
        store_internal(ltime, warnings);
        return TYPE_WARN_OUT_OF_RANGE;
    }

    /*
      Both number_to_time() call and negative nanoseconds value
      affect ltime->neg, hence "|=" to combine them:
    */
    if ((ltime->neg |= (nanoseconds < 0)))
        nanoseconds = -nanoseconds;

    ltime->second_part = 0;
    bool round_error = time_add_nanoseconds_with_round(ltime, nanoseconds,
                       warnings);
    return round_error ? time_warning_to_type_conversion_status(*warnings)
           : TYPE_OK;
}


type_conversion_status Field_time_common::store_time(MYSQL_TIME *ltime,
        uint8 dec_arg __attribute__((unused)))
{
    /* Check if seconds or minutes are out of range */
    if (ltime->second >= 60 || ltime->minute >= 60) {
        set_warnings(ErrConvString(ltime, decimals()),
                     MYSQL_TIME_WARN_OUT_OF_RANGE);
        reset();
        return TYPE_WARN_OUT_OF_RANGE;
    }

    int warnings = 0;
    return store_internal_with_round(ltime, &warnings);
}


type_conversion_status Field_time_common::store_internal_with_round(MYSQL_TIME *ltime, int *warnings)
{
    if (my_time_round(ltime, dec))
        return TYPE_WARN_OUT_OF_RANGE;

    return store_internal(ltime, warnings);
}


String *Field_time_common::val_str(String *val_buffer,
                                   String *val_ptr __attribute__((unused)))
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    MYSQL_TIME ltime;
    val_buffer->alloc(MAX_DATE_STRING_REP_LENGTH);
    val_buffer->set_charset(&my_charset_numeric);

    if (get_time(&ltime)) {
        DBUG_ASSERT(0);
        set_zero_time(&ltime, MYSQL_TIMESTAMP_TIME);
    }

    make_time((DATE_TIME_FORMAT *) 0, &ltime, val_buffer, dec);
    return val_buffer;
}


/**
  For a column for TIME type, get_date() takes the time
  value of the field, adds current date to it and returns
  the result as a DATETIME value.
*/

bool Field_time_common::get_date(MYSQL_TIME *ltime, uint fuzzydate)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    MYSQL_TIME tm;

    if (get_time(&tm)) {
        DBUG_ASSERT(0);
        set_zero_time(ltime, MYSQL_TIMESTAMP_TIME);
    }

    time_to_datetime(table ? table->in_use : current_thd, &tm, ltime);
    return false;
}


longlong Field_time_common::val_date_temporal()
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    MYSQL_TIME time, datetime;

    if (get_time(&time)) {
        DBUG_ASSERT(0); // Field_time*::get_time should not fail
        return 0;
    }

    time_to_datetime(table ? table->in_use : current_thd, &time, &datetime);
    return TIME_to_longlong_datetime_packed(&datetime);
}


bool Field_time_common::send_binary(Protocol *protocol)
{
    MYSQL_TIME ltime;

    if (get_time(&ltime)) {
        DBUG_ASSERT(0);
        set_zero_time(&ltime, MYSQL_TIMESTAMP_TIME);
    }

    ltime.day = ltime.hour / 24;        // Move hours to days
    ltime.hour -= ltime.day * 24;
    return protocol->store_time(&ltime, 0);
}


/****************************************************************************
** time type
** In string context: HH:MM:SS
** In number context: HHMMSS
** Stored as a 3 byte unsigned int
****************************************************************************/

type_conversion_status Field_time::store_internal(const MYSQL_TIME *ltime, int *warnings)
{
    long tmp = ((ltime->month ? 0 : ltime->day * 24L) + ltime->hour) * 10000L +
               (ltime->minute * 100 + ltime->second);

    if (ltime->neg)
        tmp = -tmp;

    int3store(ptr, tmp);
    return TYPE_OK;
}


type_conversion_status Field_time::store_packed(longlong nr)
{
    MYSQL_TIME ltime;
    TIME_from_longlong_time_packed(&ltime, nr);
    return Field_time::store_time(&ltime, 0);
}


longlong Field_time::val_time_temporal()
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    MYSQL_TIME ltime;
    return get_time(&ltime) ? 0 : TIME_to_longlong_time_packed(&ltime);
}


longlong Field_time::val_int()
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    return (longlong) sint3korr(ptr);
}


bool Field_time::get_time(MYSQL_TIME *ltime)
{
    long tmp = (long) sint3korr(ptr);

    if ((ltime->neg = tmp < 0))
        tmp = -tmp;

    ltime->year = ltime->month = ltime->day = 0;
    TIME_set_hhmmss(ltime, tmp);
    ltime->second_part = 0;
    ltime->time_type = MYSQL_TIMESTAMP_TIME;
    return false;
}


int Field_time::cmp(const uchar *a_ptr, const uchar *b_ptr)
{
    int32 a, b;
    a = (int32) sint3korr(a_ptr);
    b = (int32) sint3korr(b_ptr);
    return (a < b) ? -1 : (a > b) ? 1 : 0;
}


void Field_time::make_sort_key(uchar *to, uint length)
{
    DBUG_ASSERT(length >= 3);
    to[0] = (uchar) (ptr[2] ^ 128);
    to[1] = ptr[1];
    to[2] = ptr[0];
}


void Field_time::sql_type(String &res) const
{
    res.set_ascii(STRING_WITH_LEN("time"));
}


/****************************************************************************
** time type with fsp
** In string context: HH:MM:SS.FFFFFF
** In number context: HHMMSS.FFFFFF
****************************************************************************/


longlong Field_timef::val_int()
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    MYSQL_TIME ltime;

    if (get_time(&ltime)) {
        DBUG_ASSERT(0);
        set_zero_time(&ltime, MYSQL_TIMESTAMP_TIME);
    }

    longlong tmp = (longlong) TIME_to_ulonglong_time_round(&ltime);
    return ltime.neg ? -tmp : tmp;
}


my_decimal *Field_timef::val_decimal(my_decimal *decimal_value)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    MYSQL_TIME ltime;

    if (get_time(&ltime)) {
        DBUG_ASSERT(0);
        set_zero_time(&ltime, MYSQL_TIMESTAMP_TIME);
    }

    return time2my_decimal(&ltime, decimal_value);
}


double Field_timef::val_real()
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    MYSQL_TIME ltime;

    if (get_time(&ltime)) {
        DBUG_ASSERT(0);
        return 0;
    }

    double tmp = TIME_to_double_time(&ltime);
    return ltime.neg ? -tmp : tmp;
}


void Field_timef::sql_type(String &res) const
{
    if (dec == 0) {
        res.set_ascii(STRING_WITH_LEN("time"));
        return;
    }

    const CHARSET_INFO *cs = res.charset();
    res.length(cs->cset->snprintf(cs, (char *) res.ptr(), res.alloced_length(),
                                  "time(%d)", dec));
}


type_conversion_status Field_timef::reset()
{
    return store_packed(0);
}


type_conversion_status Field_timef::store_packed(longlong nr)
{
    my_time_packed_to_binary(nr, ptr, dec);
    return TYPE_OK;
}


longlong Field_timef::val_time_temporal()
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    return my_time_packed_from_binary(ptr, dec);
}


type_conversion_status Field_timef::store_internal(const MYSQL_TIME *ltime, int *warnings)
{
    type_conversion_status rc = store_packed(TIME_to_longlong_time_packed(ltime));

    if (rc == TYPE_OK && non_zero_date(ltime)) {
        /*
          The DATE part got lost; we warn, like in Field_newdate::store_internal,
          and trigger some code in get_mm_leaf()
          (see err==TYPE_NOTE_TIME_TRUNCATED there).
        */
        *warnings |= MYSQL_TIME_NOTE_TRUNCATED;
        rc = TYPE_NOTE_TIME_TRUNCATED;
    }

    return rc;
}


bool Field_timef::get_time(MYSQL_TIME *ltime)
{
    longlong tmp = val_time_temporal();
    TIME_from_longlong_time_packed(ltime, tmp);
    return false;
}


/****************************************************************************
** year type
** Save in a byte the year 0, 1901->2155
** Can handle 2 byte or 4 byte years!
****************************************************************************/

type_conversion_status Field_year::store(const char *from, uint len, const CHARSET_INFO *cs)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    char *end;
    int conv_error;
    type_conversion_status ret = TYPE_OK;
    longlong nr = cs->cset->strntoull10rnd(cs, from, len, 0, &end, &conv_error);

    if (nr < 0 || (nr >= 100 && nr <= 1900) || nr > 2155 ||
            conv_error == MY_ERRNO_ERANGE) {
        *ptr = 0;
        set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
        return TYPE_WARN_OUT_OF_RANGE;

    } else if (conv_error)
        ret = TYPE_ERR_BAD_VALUE;

    if (table->in_use->count_cuted_fields) {
        ret = check_int(cs, from, len, end, conv_error);

        if (ret != TYPE_OK) {
            if (ret == TYPE_ERR_BAD_VALUE) { /* empty or incorrect string */
                *ptr = 0;
                return TYPE_WARN_OUT_OF_RANGE;
            }

            ret = TYPE_WARN_OUT_OF_RANGE;
        }
    }

    if (nr != 0 || len != 4) {
        if (nr < YY_PART_YEAR)
            nr += 100;					// 2000 - 2069
        else if (nr > 1900)
            nr -= 1900;
    }

    *ptr = (char) (uchar) nr;
    return ret;
}


type_conversion_status Field_year::store(double nr)
{
    if (nr < 0.0 || nr >= 2155.0) {
        (void) Field_year::store((longlong) - 1, FALSE);
        return TYPE_WARN_OUT_OF_RANGE;
    }

    return Field_year::store((longlong) nr, FALSE);
}


type_conversion_status Field_year::store_time(MYSQL_TIME *ltime,
        uint8 dec_arg __attribute__((unused)))
{
    if (ltime->time_type != MYSQL_TIMESTAMP_DATETIME &&
            ltime->time_type != MYSQL_TIMESTAMP_DATE) {
        /* Convert time to datetime, then store year of the result */
        THD *thd = table ? table->in_use : current_thd;
        MYSQL_TIME ltime2;
        time_to_datetime(thd, ltime, &ltime2);
        return store(ltime2.year, 0);
    }

    return store(ltime->year, 0);
}


type_conversion_status Field_year::store(longlong nr, bool unsigned_val)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;

    if (nr < 0 || (nr >= 100 && nr <= 1900) || nr > 2155) {
        *ptr = 0;
        set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
        return TYPE_WARN_OUT_OF_RANGE;
    }

    if (nr != 0 || field_length != 4) {	// 0000 -> 0; 00 -> 2000
        if (nr < YY_PART_YEAR)
            nr += 100;					// 2000 - 2069
        else if (nr > 1900)
            nr -= 1900;
    }

    *ptr = (char) (uchar) nr;
    return TYPE_OK;
}


bool Field_year::send_binary(Protocol *protocol)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    ulonglong tmp = Field_year::val_int();
    return protocol->store_short(tmp);
}


double Field_year::val_real(void)
{
    return (double) Field_year::val_int();
}


longlong Field_year::val_int(void)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    DBUG_ASSERT(field_length == 2 || field_length == 4);
    int tmp = (int) ptr[0];

    if (field_length != 4)
        tmp %= 100;					// Return last 2 char
    else if (tmp)
        tmp += 1900;

    return (longlong) tmp;
}


String *Field_year::val_str(String *val_buffer,
                            String *val_ptr __attribute__((unused)))
{
    DBUG_ASSERT(field_length < 5);
    val_buffer->alloc(5);
    val_buffer->length(field_length);
    char *to = (char *) val_buffer->ptr();
    sprintf(to, field_length == 2 ? "%02d" : "%04d", (int) Field_year::val_int());
    val_buffer->set_charset(&my_charset_numeric);
    return val_buffer;
}


void Field_year::sql_type(String &res) const
{
    const CHARSET_INFO *cs = res.charset();
    res.length(cs->cset->snprintf(cs, (char *)res.ptr(), res.alloced_length(),
                                  "year(%d)", (int) field_length));
}


/****************************************************************************
** The new date type
** Stored as 3 bytes
** In number context: YYYYMMDD
****************************************************************************/

ulonglong Field_newdate::date_flags(const THD *thd)
{
    return TIME_FUZZY_DATE | thd->datetime_flags();
}


type_conversion_status Field_newdate::store_internal(const MYSQL_TIME *ltime, int *warnings)
{
    long tmp = ltime->day + ltime->month * 32 + ltime->year * 16 * 32;
    int3store(ptr, tmp);

    if (non_zero_time(ltime)) {
        *warnings |= MYSQL_TIME_NOTE_TRUNCATED;
        return TYPE_NOTE_TIME_TRUNCATED;
    }

    return TYPE_OK;
}


bool Field_newdate::get_date_internal(MYSQL_TIME *ltime)
{
    uint32 tmp = (uint32) uint3korr(ptr);
    ltime->day =   tmp & 31;
    ltime->month = (tmp >> 5) & 15;
    ltime->year =  (tmp >> 9);
    ltime->time_type = MYSQL_TIMESTAMP_DATE;
    ltime->hour = ltime->minute = ltime->second = ltime->second_part = ltime->neg = 0;
    return false;
}


type_conversion_status Field_newdate::store_packed(longlong nr)
{
    int warnings = 0;
    MYSQL_TIME ltime;
    TIME_from_longlong_date_packed(&ltime, nr);
    return store_internal(&ltime, &warnings);
}


bool Field_newdate::send_binary(Protocol *protocol)
{
    MYSQL_TIME ltime;
    get_date(&ltime, 0);
    return protocol->store_date(&ltime);
}


longlong Field_newdate::val_int()
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    ulong j = uint3korr(ptr);
    j = (j % 32L) + (j / 32L % 16L) * 100L + (j / (16L * 32L)) * 10000L;
    return (longlong) j;
}


longlong Field_newdate::val_date_temporal()
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    MYSQL_TIME ltime;
    return get_date_internal(&ltime) ? 0 : TIME_to_longlong_date_packed(&ltime);
}


longlong Field_newdate::val_time_temporal()
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    return 0;
}


String *Field_newdate::val_str(String *val_buffer,
                               String *val_ptr __attribute__((unused)))
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    val_buffer->alloc(field_length);
    val_buffer->length(field_length);
    uint32 tmp = (uint32) uint3korr(ptr);
    int part;
    char *pos = (char *) val_buffer->ptr() + 10;
    /* Open coded to get more speed */
    *pos-- = 0;					// End NULL
    part = (int) (tmp & 31);
    *pos-- = (char) ('0' + part % 10);
    *pos-- = (char) ('0' + part / 10);
    *pos-- = '-';
    part = (int) (tmp >> 5 & 15);
    *pos-- = (char) ('0' + part % 10);
    *pos-- = (char) ('0' + part / 10);
    *pos-- = '-';
    part = (int) (tmp >> 9);
    *pos-- = (char) ('0' + part % 10);
    part /= 10;
    *pos-- = (char) ('0' + part % 10);
    part /= 10;
    *pos-- = (char) ('0' + part % 10);
    part /= 10;
    *pos =   (char) ('0' + part);
    val_buffer->set_charset(&my_charset_numeric);
    return val_buffer;
}


bool Field_newdate::get_date(MYSQL_TIME *ltime, uint fuzzydate)
{
    return get_internal_check_zero(ltime, fuzzydate) ||
           check_fuzzy_date(ltime, fuzzydate);
}


int Field_newdate::cmp(const uchar *a_ptr, const uchar *b_ptr)
{
    uint32 a, b;
    a = (uint32) uint3korr(a_ptr);
    b = (uint32) uint3korr(b_ptr);
    return (a < b) ? -1 : (a > b) ? 1 : 0;
}


void Field_newdate::make_sort_key(uchar *to, uint length)
{
    DBUG_ASSERT(length >= 3);
    to[0] = ptr[2];
    to[1] = ptr[1];
    to[2] = ptr[0];
}


void Field_newdate::sql_type(String &res) const
{
    res.set_ascii(STRING_WITH_LEN("date"));
}


/****************************************************************************
** datetime type
** In string context: YYYY-MM-DD HH:MM:DD
** In number context: YYYYMMDDHHMMDD
** Stored as a 8 byte unsigned int. Should sometimes be change to a 6 byte int.
****************************************************************************/


ulonglong Field_datetime::date_flags(const THD *thd)
{
    return TIME_FUZZY_DATE | thd->datetime_flags();
}


void Field_datetime::store_timestamp_internal(const timeval *tm)
{
    MYSQL_TIME mysql_time;
    THD *thd = current_thd;
    thd->variables.time_zone->gmt_sec_to_TIME(&mysql_time, *tm);
    thd->time_zone_used = true;
    int error = 0;
    store_internal(&mysql_time, &error);
}


/**
  Store a DATETIME in a 8-byte integer to record.

  @param table  Table
  @param tmp    The number, in YYYYMMDDhhmmss format
  @param ptr    Where to store to
*/
static inline type_conversion_status datetime_store_internal(TABLE *table, ulonglong tmp, uchar *ptr)
{
#ifdef WORDS_BIGENDIAN

    if (table && table->s->db_low_byte_first)
        int8store(ptr, tmp);

    else
#endif
        longlongstore(ptr, tmp);

    return TYPE_OK;
}


/**
  Read a DATETIME from record to a 8-byte integer

  @param table  Table
  @param ptr    Where to read from
  @retval       An integer in format YYYYMMDDhhmmss
*/
static inline longlong datetime_get_internal(TABLE *table, uchar *ptr)
{
    longlong tmp;
#ifdef WORDS_BIGENDIAN

    if (table && table->s->db_low_byte_first)
        tmp = sint8korr(ptr);
    else
#endif
        longlongget(tmp, ptr);

    return tmp;
}


bool Field_datetime::get_date_internal(MYSQL_TIME *ltime)
{
    longlong tmp = datetime_get_internal(table, ptr);
    ltime->time_type = MYSQL_TIMESTAMP_DATETIME;
    ltime->neg = 0;
    ltime->second_part = 0;
    TIME_set_yymmdd(ltime, (uint) (tmp / 1000000LL));
    TIME_set_hhmmss(ltime, (uint) (tmp % 1000000LL));
    return false;
}


type_conversion_status Field_datetime::store_internal(const MYSQL_TIME *ltime, int *warnings)
{
    ulonglong tmp = TIME_to_ulonglong_datetime(ltime);
    return datetime_store_internal(table, tmp, ptr);
}


type_conversion_status Field_datetime::store(longlong nr, bool unsigned_val)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    MYSQL_TIME ltime;
    int warnings;
    type_conversion_status error = TYPE_OK;
    longlong tmp = convert_number_to_datetime(nr, unsigned_val,
                   &ltime, &warnings);

    if (tmp == LL(-1))
        error = TYPE_ERR_BAD_VALUE;
    else {
        error = time_warning_to_type_conversion_status(warnings);
        datetime_store_internal(table, tmp, ptr);
    }

    if (warnings)
        set_warnings(ErrConvString(nr, unsigned_val), warnings);

    return error;
}


type_conversion_status Field_datetime::store_packed(longlong nr)
{
    MYSQL_TIME ltime;
    TIME_from_longlong_datetime_packed(&ltime, nr);
    return Field_datetime::store_time(&ltime, 0);
}


longlong Field_datetime::val_int()
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    return datetime_get_internal(table, ptr);
}


/*
  We don't reuse the parent method for performance purposes,
  to avoid convertion from number to MYSQL_TIME.
  Using my_datetime_number_to_str() instead of my_datetime_to_str().
*/
String *Field_datetime::val_str(String *val_buffer,
                                String *val_ptr __attribute__((unused)))
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    val_buffer->alloc(field_length + 1);
    val_buffer->set_charset(&my_charset_numeric);
    val_buffer->length(MAX_DATETIME_WIDTH);
    longlong tmp = datetime_get_internal(table, ptr);
    val_buffer->length(my_datetime_number_to_str((char *) val_buffer->ptr(),
                       tmp));
    return val_buffer;
}


bool Field_datetime::get_date(MYSQL_TIME *ltime, uint fuzzydate)
{
    return get_internal_check_zero(ltime, fuzzydate) ||
           check_fuzzy_date(ltime, fuzzydate);
}


int Field_datetime::cmp(const uchar *a_ptr, const uchar *b_ptr)
{
    longlong a, b;
#ifdef WORDS_BIGENDIAN

    if (table && table->s->db_low_byte_first) {
        a = sint8korr(a_ptr);
        b = sint8korr(b_ptr);

    } else
#endif
    {
        longlongget(a, a_ptr);
        longlongget(b, b_ptr);
    }

    return ((ulonglong) a < (ulonglong) b) ? -1 :
           ((ulonglong) a > (ulonglong) b) ? 1 : 0;
}

void Field_datetime::make_sort_key(uchar *to, uint length)
{
    const int pack_length = PACK_LENGTH;
    const int to_length = min<uint>(pack_length, length);
#ifdef WORDS_BIGENDIAN

    if (!table || !table->s->db_low_byte_first)
        copy_integer<true>(to, to_length, ptr, pack_length, true);
    else
#endif
        copy_integer<false>(to, to_length, ptr, pack_length, true);
}


void Field_datetime::sql_type(String &res) const
{
    res.set_ascii(STRING_WITH_LEN("datetime"));
}

/****************************************************************************
** datetimef type
** In string context: YYYY-MM-DD HH:MM:DD.FFFFFF
** In number context: YYYYMMDDHHMMDD.FFFFFF
** Stored as a 8 byte value.
****************************************************************************/


ulonglong Field_datetimef::date_flags(const THD *thd)
{
    return TIME_FUZZY_DATE | thd->datetime_flags();
}


void Field_datetimef::store_timestamp_internal(const timeval *tm)
{
    MYSQL_TIME mysql_time;
    THD *thd = current_thd;
    thd->variables.time_zone->gmt_sec_to_TIME(&mysql_time, *tm);
    thd->time_zone_used = true;
    int warnings = 0;
    store_internal(&mysql_time, &warnings);
}



bool Field_datetimef::get_date(MYSQL_TIME *ltime, uint fuzzydate)
{
    return get_internal_check_zero(ltime, fuzzydate) ||
           check_fuzzy_date(ltime, fuzzydate);
}


void Field_datetimef::sql_type(String &res) const
{
    if (dec == 0) {
        res.set_ascii(STRING_WITH_LEN("datetime"));
        return;
    }

    const CHARSET_INFO *cs = res.charset();
    res.length(cs->cset->snprintf(cs, (char *) res.ptr(), res.alloced_length(),
                                  "datetime(%d)", dec));
}


bool Field_datetimef::get_date_internal(MYSQL_TIME *ltime)
{
    TIME_from_longlong_datetime_packed(ltime, val_date_temporal());
    return false;
}


type_conversion_status Field_datetimef::store_internal(const MYSQL_TIME *ltime, int *warnings)
{
    store_packed(TIME_to_longlong_datetime_packed(ltime));
    return TYPE_OK;
}


type_conversion_status Field_datetimef::reset()
{
    store_packed(0);
    return TYPE_OK;
}


longlong Field_datetimef::val_date_temporal()
{
    return my_datetime_packed_from_binary(ptr, dec);
}


type_conversion_status Field_datetimef::store_packed(longlong nr)
{
    my_datetime_packed_to_binary(nr, ptr, dec);
    return TYPE_OK;
}



/****************************************************************************
** string type
** A string may be varchar or binary
****************************************************************************/

/**
  Report "not well formed" or "cannot convert" error
  after storing a character string info a field.

  As of version 5.0 both cases return the same error:

      "Invalid string value: 'xxx' for column 't' at row 1"

  Future versions will possibly introduce a new error message:

      "Cannot convert character string: 'xxx' for column 't' at row 1"

  @param  well_formed_error_pos      position of the first non-wellformed
                                     character in the source string
  @param  cannot_convert_error_pos   position of the first non-convertable
                                     character in the source string
  @param  from_end_pos               position where conversion stopped in
                                     the source string
  @param  end                        end of the source string
  @param  count_spaces               treat trailing spaces as important data
  @param  cs                         character set of the string

  @return TYPE_OK, TYPE_NOTE_TRUNCATED, TYPE_WARN_TRUNCATED

*/

type_conversion_status Field_longstr::check_string_copy_error(const char *well_formed_error_pos,
        const char *cannot_convert_error_pos,
        const char *from_end_pos,
        const char *end,
        bool count_spaces,
        const CHARSET_INFO *cs) const
{
    const char *pos;
    char tmp[32];
    THD *thd = table->in_use;

    if (!(pos = well_formed_error_pos) &&
            !(pos = cannot_convert_error_pos))
        return report_if_important_data(from_end_pos, end, count_spaces);

    convert_to_printable(tmp, sizeof(tmp), pos, (end - pos), cs, 6);
    push_warning_printf(thd,
                        Sql_condition::WARN_LEVEL_WARN,
                        ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
                        ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
                        "string", tmp, field_name,
                        thd->get_stmt_da()->current_row_for_warning());
    return TYPE_WARN_TRUNCATED;
}


/*
  Check if we lost any important data and send a truncation error/warning

  SYNOPSIS
    Field_longstr::report_if_important_data()
    pstr                     - Truncated rest of string
    end                      - End of truncated string
    count_spaces             - Treat traling spaces as important data

  RETURN VALUES
    false  - None was truncated (or we don't count cut fields)
    true   - Some bytes were truncated

  NOTE
    Check if we lost any important data (anything in a binary string,
    or any non-space in others). If only trailing spaces was lost,
    send a truncation note, otherwise send a truncation error.
    Silently ignore traling spaces if the count_space parameter is FALSE.
*/

type_conversion_status Field_longstr::report_if_important_data(const char *pstr, const char *end,
        bool count_spaces) const
{
    if ((pstr < end) && table->in_use->count_cuted_fields) {
        if (test_if_important_data(field_charset, pstr, end)) {
            if (table->in_use->abort_on_warning)
                set_warning(Sql_condition::WARN_LEVEL_WARN, ER_DATA_TOO_LONG, 1);
            else
                set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);

            return TYPE_WARN_TRUNCATED;

        } else if (count_spaces) {
            /* If we lost only spaces then produce a NOTE, not a WARNING */
            set_warning(Sql_condition::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED, 1);
            return TYPE_NOTE_TRUNCATED;
        }
    }

    return TYPE_OK;
}


/* Copy a string and fill with space */

type_conversion_status Field_string::store(const char *from, uint length, const CHARSET_INFO *cs)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    uint copy_length;
    const char *well_formed_error_pos;
    const char *cannot_convert_error_pos;
    const char *from_end_pos;
    /* See the comment for Field_long::store(long long) */
    DBUG_ASSERT(table->in_use == current_thd);
    copy_length = well_formed_copy_nchars(field_charset,
                                          (char *) ptr, field_length,
                                          cs, from, length,
                                          field_length / field_charset->mbmaxlen,
                                          &well_formed_error_pos,
                                          &cannot_convert_error_pos,
                                          &from_end_pos);

    /* Append spaces if the string was shorter than the field. */
    if (copy_length < field_length)
        field_charset->cset->fill(field_charset, (char *) ptr + copy_length,
                                  field_length - copy_length,
                                  field_charset->pad_char);

    return check_string_copy_error(well_formed_error_pos,
                                   cannot_convert_error_pos, from_end_pos,
                                   from + length, false, cs);
}


/**
  Store double value in Field_string or Field_varstring.

  Pretty prints double number into field_length characters buffer.

  @param nr            number
*/

type_conversion_status Field_str::store(double nr)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE];
    uint local_char_length = field_length / charset()->mbmaxlen;
    size_t length = 0;
    my_bool error = (local_char_length == 0);

    // my_gcvt() requires width > 0, and we may have a CHAR(0) column.
    if (!error)
        length = my_gcvt(nr, MY_GCVT_ARG_DOUBLE, local_char_length, buff, &error);

    if (error) {
        if (table->in_use->abort_on_warning)
            set_warning(Sql_condition::WARN_LEVEL_WARN, ER_DATA_TOO_LONG, 1);
        else
            set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
    }

    return store(buff, length, &my_charset_numeric);
}


uint Field::is_equal(Create_field *new_field)
{
    return (new_field->sql_type == real_type());
}


uint Field_str::is_equal(Create_field *new_field)
{
    if (field_flags_are_binary() != new_field->field_flags_are_binary())
        return 0;

    return ((new_field->sql_type == real_type()) &&
            new_field->charset == field_charset &&
            new_field->length == max_display_length());
}


type_conversion_status Field_string::store(longlong nr, bool unsigned_val)
{
    char buff[64];
    int  l;
    const CHARSET_INFO *cs = charset();
    l = (cs->cset->longlong10_to_str)(cs, buff, sizeof(buff),
                                      unsigned_val ? 10 : -10, nr);
    return Field_string::store(buff, (uint)l, cs);
}


type_conversion_status Field_longstr::store_decimal(const my_decimal *d)
{
    char buff[DECIMAL_MAX_STR_LENGTH + 1];
    String str(buff, sizeof(buff), &my_charset_numeric);
    my_decimal2string(E_DEC_FATAL_ERROR, d, 0, 0, 0, &str);
    return store(str.ptr(), str.length(), str.charset());
}

uint32 Field_longstr::max_data_length() const
{
    return field_length + (field_length > 255 ? 2 : 1);
}


double Field_string::val_real(void)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    int error;
    char *end;
    const CHARSET_INFO *cs = charset();
    double result;
    result =  my_strntod(cs, (char *) ptr, field_length, &end, &error);

    if (!table->in_use->no_errors &&
            (error || (field_length != (uint32)(end - (char *) ptr) &&
                       !check_if_only_end_space(cs, end,
                               (char *) ptr + field_length)))) {
        ErrConvString err((char *) ptr, field_length, cs);
        push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
                            ER_TRUNCATED_WRONG_VALUE,
                            ER(ER_TRUNCATED_WRONG_VALUE), "DOUBLE",
                            err.ptr());
    }

    return result;
}


longlong Field_string::val_int(void)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    int error;
    char *end;
    const CHARSET_INFO *cs = charset();
    longlong result;
    result = my_strntoll(cs, (char *) ptr, field_length, 10, &end, &error);

    if (!table->in_use->no_errors &&
            (error || (field_length != (uint32)(end - (char *) ptr) &&
                       !check_if_only_end_space(cs, end,
                               (char *) ptr + field_length)))) {
        ErrConvString err((char *) ptr, field_length, cs);
        push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
                            ER_TRUNCATED_WRONG_VALUE,
                            ER(ER_TRUNCATED_WRONG_VALUE),
                            "INTEGER", err.ptr());
    }

    return result;
}


String *Field_string::val_str(String *val_buffer __attribute__((unused)),
                              String *val_ptr)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    /* See the comment for Field_long::store(long long) */
//   DBUG_ASSERT(table->in_use == current_thd);
    uint length;
//   if (table->in_use->variables.sql_mode &
//       MODE_PAD_CHAR_TO_FULL_LENGTH)
//     length= my_charpos(field_charset, ptr, ptr + field_length,
//                        field_length / field_charset->mbmaxlen);
//   else
    length = field_charset->cset->lengthsp(field_charset, (const char *) ptr,
                                           field_length);
    val_ptr->set((const char *) ptr, length, field_charset);
    return val_ptr;
}


my_decimal *Field_string::val_decimal(my_decimal *decimal_value)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    int err = str2my_decimal(E_DEC_FATAL_ERROR, (char *) ptr, field_length,
                             charset(), decimal_value);

    if (!table->in_use->no_errors && err) {
        ErrConvString errmsg((char *) ptr, field_length, charset());
        push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
                            ER_TRUNCATED_WRONG_VALUE,
                            ER(ER_TRUNCATED_WRONG_VALUE),
                            "DECIMAL", errmsg.ptr());
    }

    return decimal_value;
}


struct Check_field_param
{
    Field *field;
};

// #ifdef HAVE_REPLICATION
// static bool
// check_field_for_37426(const void *param_arg)
// {
//   Check_field_param *param= (Check_field_param*) param_arg;
//   DBUG_ASSERT(param->field->real_type() == MYSQL_TYPE_STRING);
//   DBUG_PRINT("debug", ("Field %s - type: %d, size: %d",
//                        param->field->field_name,
//                        param->field->real_type(),
//                        param->field->row_pack_length()));
//   return param->field->row_pack_length() > 255;
// }
// #endif

bool Field_string::compatible_field_size(uint field_metadata,
        Relay_log_info *rli_arg,
        uint16 mflags __attribute__((unused)),
        int *order_var)
{
// #ifdef HAVE_REPLICATION
//   const Check_field_param check_param = { this };
//   if (!is_mts_worker(rli_arg->info_thd) && rpl_master_has_bug(rli_arg, 37426, TRUE,
//                          check_field_for_37426, &check_param))
//     return FALSE;                        // Not compatible field sizes
// #endif
    return Field::compatible_field_size(field_metadata, rli_arg, mflags, order_var);
}


int Field_string::cmp(const uchar *a_ptr, const uchar *b_ptr)
{
    uint a_len, b_len;

    if (field_charset->mbmaxlen != 1) {
        uint char_len = field_length / field_charset->mbmaxlen;
        a_len = my_charpos(field_charset, a_ptr, a_ptr + field_length, char_len);
        b_len = my_charpos(field_charset, b_ptr, b_ptr + field_length, char_len);

    } else
        a_len = b_len = field_length;

    /*
      We have to remove end space to be able to compare multi-byte-characters
      like in latin_de 'ae' and 0xe4
    */
    return field_charset->coll->strnncollsp(field_charset,
                                            a_ptr, a_len,
                                            b_ptr, b_len,
                                            0);
}


void Field_string::make_sort_key(uchar *to, uint length)
{
    uint tmp __attribute__((unused)) =
        field_charset->coll->strnxfrm(field_charset,
                                      to, length, char_length(),
                                      ptr, field_length,
                                      MY_STRXFRM_PAD_WITH_SPACE |
                                      MY_STRXFRM_PAD_TO_MAXLEN);
    DBUG_ASSERT(tmp == length);
}


void Field_string::sql_type(String &res) const
{
    THD *thd = table->in_use;
    const CHARSET_INFO *cs = res.charset();
    ulong length;
    length = cs->cset->snprintf(cs, (char *) res.ptr(),
                                res.alloced_length(), "%s(%d)",
                                ((type() == MYSQL_TYPE_VAR_STRING &&
                                  !thd->variables.new_mode) ?
                                 (has_charset() ? "varchar" : "varbinary") :
                                 (has_charset() ? "char" : "binary")),
                                (int) field_length / charset()->mbmaxlen);
    res.length(length);

    if ((thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) &&
            has_charset() && (charset()->state & MY_CS_BINSORT))
        res.append(STRING_WITH_LEN(" binary"));
}


uchar *Field_string::pack(uchar *to, const uchar *from,
                          uint max_length,
                          bool low_byte_first __attribute__((unused)))
{
    uint length =      min(field_length, max_length);
    uint local_char_length = max_length / field_charset->mbmaxlen;
    DBUG_PRINT("debug", ("Packing field '%s' - length: %u ", field_name, length));

    if (length > local_char_length)
        local_char_length = my_charpos(field_charset, from, from + length,
                                       local_char_length);

    set_if_smaller(length, local_char_length);

    /*
       TODO: change charset interface to add a new function that does
             the following or add a flag to lengthsp to do it itself
             (this is for not packing padding adding bytes in BINARY
             fields).
    */
    if (field_charset->mbmaxlen == 1) {
        while (length && from[length - 1] == field_charset->pad_char)
            length --;

    } else
        length = field_charset->cset->lengthsp(field_charset, (const char *) from, length);

    // Length always stored little-endian
    *to++ = (uchar) length;

    if (field_length > 255)
        *to++ = (uchar) (length >> 8);

    // Store the actual bytes of the string
    memcpy(to, from, length);
    return to + length;
}


/**
   Unpack a string field from row data.

   This method is used to unpack a string field from a master whose size
   of the field is less than that of the slave. Note that there can be a
   variety of field types represented with this class. Certain types like
   ENUM or SET are processed differently. Hence, the upper byte of the
   @c param_data argument contains the result of field->real_type() from
   the master.

   @note For information about how the length is packed, see @c
   Field_string::do_save_field_metadata

   @param   to         Destination of the data
   @param   from       Source of the data
   @param   param_data Real type (upper) and length (lower) values

   @return  New pointer into memory based on from + length of the data
*/
const uchar *Field_string::unpack(uchar *to,
                                  const uchar *from,
                                  uint param_data,
                                  bool low_byte_first __attribute__((unused)))
{
    uint from_length, length;

    /*
      Compute the declared length of the field on the master. This is
      used to decide if one or two bytes should be read as length.
     */
    if (param_data)
        from_length = (((param_data >> 4) & 0x300) ^ 0x300) + (param_data & 0x00ff);
    else
        from_length = field_length;

    DBUG_PRINT("debug",
               ("param_data: 0x%x, field_length: %u, from_length: %u",
                param_data, field_length, from_length));

    /*
      Compute the actual length of the data by reading one or two bits
      (depending on the declared field length on the master).
     */
    if (from_length > 255) {
        length = uint2korr(from);
        from += 2;

    } else
        length = (uint) * from++;

    memcpy(to, from, length);
    // Pad the string with the pad character of the fields charset
    field_charset->cset->fill(field_charset, (char *) to + length, field_length - length, field_charset->pad_char);
    return from + length;
}


/**
   Save the field metadata for string fields.

   Saves the real type in the first byte and the field length in the
   second byte of the field metadata array at index of *metadata_ptr and
   *(metadata_ptr + 1).

   @note In order to be able to handle lengths exceeding 255 and be
   backwards-compatible with pre-5.1.26 servers, an extra two bits of
   the length has been added to the metadata in such a way that if
   they are set, a new unrecognized type is generated.  This will
   cause pre-5.1-26 servers to stop due to a field type mismatch,
   while new servers will be able to extract the extra bits. If the
   length is <256, there will be no difference and both a new and an
   old server will be able to handle it.

   @note The extra two bits are added to bits 13 and 14 of the
   parameter data (with 1 being the least siginficant bit and 16 the
   most significant bit of the word) by xoring the extra length bits
   with the real type.  Since all allowable types have 0xF as most
   significant bits of the metadata word, lengths <256 will not affect
   the real type at all, while all other values will result in a
   non-existant type in the range 17-244.

   @see Field_string::unpack

   @param   metadata_ptr   First byte of field metadata

   @returns number of bytes written to metadata_ptr
*/
int Field_string::do_save_field_metadata(uchar *metadata_ptr)
{
    DBUG_ASSERT(field_length < 1024);
    DBUG_ASSERT((real_type() & 0xF0) == 0xF0);
    DBUG_PRINT("debug", ("field_length: %u, real_type: %u",
                         field_length, real_type()));
    *metadata_ptr = (real_type() ^ ((field_length & 0x300) >> 4));
    *(metadata_ptr + 1) = field_length & 0xFF;
    return 2;
}


uint Field_string::packed_col_length(const uchar *data_ptr, uint length)
{
    if (length > 255)
        return uint2korr(data_ptr) + 2;

    return (uint) * data_ptr + 1;
}


uint Field_string::max_packed_col_length(uint max_length)
{
    return (max_length > 255 ? 2 : 1) + max_length;
}


uint Field_string::get_key_image(uchar *buff, uint length, imagetype type_arg)
{
    uint bytes = my_charpos(field_charset, (char *) ptr,
                            (char *) ptr + field_length,
                            length / field_charset->mbmaxlen);
    memcpy(buff, ptr, bytes);

    if (bytes < length)
        field_charset->cset->fill(field_charset, (char *) buff + bytes,
                                  length - bytes, field_charset->pad_char);

    return bytes;
}


Field *Field_string::new_field(MEM_ROOT *root, TABLE *new_table,
                               bool keep_type)
{
    Field *field;

    if (type() != MYSQL_TYPE_VAR_STRING || keep_type)
        field = Field::new_field(root, new_table, keep_type);
    else if ((field = new Field_varstring(field_length, maybe_null(), field_name,
                                          new_table->s, charset()))) {
        /*
          Old VARCHAR field which should be modified to a VARCHAR on copy
          This is done to ensure that ALTER TABLE will convert old VARCHAR fields
          to now VARCHAR fields.
        */
        field->init(new_table);
        /*
          Normally orig_table is different from table only if field was created
          via ::new_field.  Here we alter the type of field, so ::new_field is
          not applicable. But we still need to preserve the original field
          metadata for the client-server protocol.
        */
        field->orig_table = orig_table;
    }

    return field;
}


/****************************************************************************
  VARCHAR type
  Data in field->ptr is stored as:
    1 or 2 bytes length-prefix-header  (from Field_varstring::length_bytes)
    data

  NOTE:
  When VARCHAR is stored in a key (for handler::index_read() etc) it's always
  stored with a 2 byte prefix. (Just like blob keys).

  Normally length_bytes is calculated as (field_length < 256 : 1 ? 2)
  The exception is if there is a prefix key field that is part of a long
  VARCHAR, in which case field_length for this may be 1 but the length_bytes
  is 2.
****************************************************************************/

const uint Field_varstring::MAX_SIZE = UINT_MAX16;

/**
   Save the field metadata for varstring fields.

   Saves the field length in the first byte. Note: may consume
   2 bytes. Caller must ensure second byte is contiguous with
   first byte (e.g. array index 0,1).

   @param   metadata_ptr   First byte of field metadata

   @returns number of bytes written to metadata_ptr
*/
int Field_varstring::do_save_field_metadata(uchar *metadata_ptr)
{
    DBUG_ASSERT(field_length <= 65535);
    int2store((char *)metadata_ptr, field_length);
    return 2;
}

type_conversion_status Field_varstring::store(const char *from, uint length,
        const CHARSET_INFO *cs)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    uint copy_length;
    const char *well_formed_error_pos;
    const char *cannot_convert_error_pos;
    const char *from_end_pos;
    copy_length = well_formed_copy_nchars(field_charset,
                                          (char *) ptr + length_bytes,
                                          field_length,
                                          cs, from, length,
                                          field_length / field_charset->mbmaxlen,
                                          &well_formed_error_pos,
                                          &cannot_convert_error_pos,
                                          &from_end_pos);

    if (length_bytes == 1)
        *ptr = (uchar) copy_length;
    else
        int2store(ptr, copy_length);

    return check_string_copy_error(well_formed_error_pos,
                                   cannot_convert_error_pos, from_end_pos,
                                   from + length, true, cs);
}


type_conversion_status Field_varstring::store(longlong nr, bool unsigned_val)
{
    char buff[64];
    uint  length;
    length = (uint) (field_charset->cset->longlong10_to_str)(field_charset,
             buff,
             sizeof(buff),
             (unsigned_val ? 10 :
              -10),
             nr);
    return Field_varstring::store(buff, length, field_charset);
}


double Field_varstring::val_real(void)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    int error;
    char *end;
    double result;
    const CHARSET_INFO *cs = charset();
    uint length = length_bytes == 1 ? (uint) * ptr : uint2korr(ptr);
    result = my_strntod(cs, (char *)ptr + length_bytes, length, &end, &error);

    if (!table->in_use->no_errors &&
            (error || (length != (uint)(end - (char *)ptr + length_bytes) &&
                       !check_if_only_end_space(cs, end, (char *)ptr + length_bytes + length)))) {
        push_numerical_conversion_warning(current_thd, (char *)ptr + length_bytes,
                                          length, cs, "DOUBLE",
                                          ER_TRUNCATED_WRONG_VALUE);
    }

    return result;
}


longlong Field_varstring::val_int(void)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    int error;
    char *end;
    const CHARSET_INFO *cs = charset();
    uint length = length_bytes == 1 ? (uint) * ptr : uint2korr(ptr);
    longlong result = my_strntoll(cs, (char *) ptr + length_bytes, length, 10,
                                  &end, &error);

    if (!table->in_use->no_errors &&
            (error || (length != (uint)(end - (char *)ptr + length_bytes) &&
                       !check_if_only_end_space(cs, end, (char *)ptr + length_bytes + length)))) {
        push_numerical_conversion_warning(current_thd, (char *)ptr + length_bytes,
                                          length, cs, "INTEGER",
                                          ER_TRUNCATED_WRONG_VALUE);
    }

    return result;
}

String *Field_varstring::val_str(String *val_buffer __attribute__((unused)),
                                 String *val_ptr)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    uint length =  length_bytes == 1 ? (uint) * ptr : uint2korr(ptr);
    val_ptr->set((const char *) ptr + length_bytes, length, field_charset);
    return val_ptr;
}


my_decimal *Field_varstring::val_decimal(my_decimal *decimal_value)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    const CHARSET_INFO *cs = charset();
    uint length = length_bytes == 1 ? (uint) * ptr : uint2korr(ptr);
    int error = str2my_decimal(E_DEC_FATAL_ERROR, (char *) ptr + length_bytes, length,
                               cs, decimal_value);

    if (!table->in_use->no_errors && error) {
        push_numerical_conversion_warning(current_thd, (char *)ptr + length_bytes,
                                          length, cs, "DECIMAL",
                                          ER_TRUNCATED_WRONG_VALUE);
    }

    return decimal_value;
}


int Field_varstring::cmp_max(const uchar *a_ptr, const uchar *b_ptr,
                             uint max_len)
{
    uint a_length, b_length;
    int diff;

    if (length_bytes == 1) {
        a_length = (uint) * a_ptr;
        b_length = (uint) * b_ptr;

    } else {
        a_length = uint2korr(a_ptr);
        b_length = uint2korr(b_ptr);
    }

    set_if_smaller(a_length, max_len);
    set_if_smaller(b_length, max_len);
    diff = field_charset->coll->strnncollsp(field_charset,
                                            a_ptr +
                                            length_bytes,
                                            a_length,
                                            b_ptr +
                                            length_bytes,
                                            b_length, 0);
    return diff;
}


/**
  @note
    varstring and blob keys are ALWAYS stored with a 2 byte length prefix
*/

int Field_varstring::key_cmp(const uchar *key_ptr, uint max_key_length)
{
    uint length =  length_bytes == 1 ? (uint) * ptr : uint2korr(ptr);
    uint local_char_length = max_key_length / field_charset->mbmaxlen;
    local_char_length = my_charpos(field_charset, ptr + length_bytes,
                                   ptr + length_bytes + length, local_char_length);
    set_if_smaller(length, local_char_length);
    return field_charset->coll->strnncollsp(field_charset,
                                            ptr + length_bytes,
                                            length,
                                            key_ptr +
                                            HA_KEY_BLOB_LENGTH,
                                            uint2korr(key_ptr), 0);
}


/**
  Compare to key segments (always 2 byte length prefix).

  @note
    This is used only to compare key segments created for index_read().
    (keys are created and compared in key.cc)
*/

int Field_varstring::key_cmp(const uchar *a, const uchar *b)
{
    return field_charset->coll->strnncollsp(field_charset,
                                            a + HA_KEY_BLOB_LENGTH,
                                            uint2korr(a),
                                            b + HA_KEY_BLOB_LENGTH,
                                            uint2korr(b),
                                            0);
}


void Field_varstring::make_sort_key(uchar *to, uint length)
{
    uint tot_length =  length_bytes == 1 ? (uint) * ptr : uint2korr(ptr);

    if (field_charset == &my_charset_bin) {
        /* Store length last in high-byte order to sort longer strings first */
        if (length_bytes == 1)
            to[length - 1] = tot_length;
        else
            mi_int2store(to + length - 2, tot_length);

        length -= length_bytes;
    }

    tot_length = field_charset->coll->strnxfrm(field_charset,
                 to, length, char_length(),
                 ptr + length_bytes, tot_length,
                 MY_STRXFRM_PAD_WITH_SPACE |
                 MY_STRXFRM_PAD_TO_MAXLEN);
    DBUG_ASSERT(tot_length == length);
}


enum ha_base_keytype Field_varstring::key_type() const
{
    enum ha_base_keytype res;

    if (binary())
        res = length_bytes == 1 ? HA_KEYTYPE_VARBINARY1 : HA_KEYTYPE_VARBINARY2;
    else
        res = length_bytes == 1 ? HA_KEYTYPE_VARTEXT1 : HA_KEYTYPE_VARTEXT2;

    return res;
}


void Field_varstring::sql_type(String &res) const
{
    THD *thd = table->in_use;
    const CHARSET_INFO *cs = res.charset();
    ulong length;
    length = cs->cset->snprintf(cs, (char *) res.ptr(),
                                res.alloced_length(), "%s(%d)",
                                (has_charset() ? "varchar" : "varbinary"),
                                (int) field_length / charset()->mbmaxlen);
    res.length(length);

    if ((thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) &&
            has_charset() && (charset()->state & MY_CS_BINSORT))
        res.append(STRING_WITH_LEN(" binary"));
}


uint32 Field_varstring::data_length()
{
    return length_bytes == 1 ? (uint32) * ptr : uint2korr(ptr);
}

/*
  Functions to create a packed row.
  Here the number of length bytes are depending on the given max_length
*/

uchar *Field_varstring::pack(uchar *to, const uchar *from,
                             uint max_length,
                             bool low_byte_first __attribute__((unused)))
{
    uint length = length_bytes == 1 ? (uint) * from : uint2korr(from);
    set_if_smaller(max_length, field_length);

    if (length > max_length)
        length = max_length;

    /* Length always stored little-endian */
    *to++ = length & 0xFF;

    if (max_length > 255)
        *to++ = (length >> 8) & 0xFF;

    /* Store bytes of string */
    if (length > 0)
        memcpy(to, from + length_bytes, length);

    return to + length;
}


/**
   Unpack a varstring field from row data.

   This method is used to unpack a varstring field from a master
   whose size of the field is less than that of the slave.

   @note
   The string length is always packed little-endian.

   @param   to         Destination of the data
   @param   from       Source of the data
   @param   param_data Length bytes from the master's field data

   @return  New pointer into memory based on from + length of the data
*/
const uchar *Field_varstring::unpack(uchar *to, const uchar *from,
                                     uint param_data,
                                     bool low_byte_first __attribute__((unused)))
{
    uint length;
    uint l_bytes = (param_data && (param_data < field_length)) ?
                   (param_data <= 255) ? 1 : 2 : length_bytes;

    if (l_bytes == 1) {
        to[0] = *from++;
        length = to[0];

        if (length_bytes == 2)
            to[1] = 0;

    } else { /* l_bytes == 2 */
        length = uint2korr(from);
        to[0] = *from++;
        to[1] = *from++;
    }

    if (length)
        memcpy(to + length_bytes, from, length);

    return from + length;
}


uint Field_varstring::packed_col_length(const uchar *data_ptr, uint length)
{
    if (length > 255)
        return uint2korr(data_ptr) + 2;

    return (uint) * data_ptr + 1;
}


uint Field_varstring::max_packed_col_length(uint max_length)
{
    return (max_length > 255 ? 2 : 1) + max_length;
}

uint Field_varstring::get_key_image(uchar *buff, uint length, imagetype type)
{
    uint f_length =  length_bytes == 1 ? (uint) * ptr : uint2korr(ptr);
    uint local_char_length = length / field_charset->mbmaxlen;
    uchar *pos = ptr + length_bytes;
    local_char_length = my_charpos(field_charset, pos, pos + f_length,
                                   local_char_length);
    set_if_smaller(f_length, local_char_length);
    /* Key is always stored with 2 bytes */
    int2store(buff, f_length);
    memcpy(buff + HA_KEY_BLOB_LENGTH, pos, f_length);

    if (f_length < length) {
        /*
          Must clear this as we do a memcmp in opt_range.cc to detect
          identical keys
        */
        memset(buff + HA_KEY_BLOB_LENGTH + f_length, 0, (length - f_length));
    }

    return HA_KEY_BLOB_LENGTH + f_length;
}


void Field_varstring::set_key_image(const uchar *buff, uint length)
{
    length = uint2korr(buff);			// Real length is here
    (void) Field_varstring::store((const char *) buff + HA_KEY_BLOB_LENGTH, length,
                                  field_charset);
}


int Field_varstring::cmp_binary(const uchar *a_ptr, const uchar *b_ptr,
                                uint32 max_length)
{
    uint32 a_length, b_length;

    if (length_bytes == 1) {
        a_length = (uint) * a_ptr;
        b_length = (uint) * b_ptr;

    } else {
        a_length = uint2korr(a_ptr);
        b_length = uint2korr(b_ptr);
    }

    set_if_smaller(a_length, max_length);
    set_if_smaller(b_length, max_length);

    if (a_length != b_length)
        return 1;

    return memcmp(a_ptr + length_bytes, b_ptr + length_bytes, a_length);
}


Field *Field_varstring::new_field(MEM_ROOT *root, TABLE *new_table,
                                  bool keep_type)
{
    Field_varstring *res = (Field_varstring *) Field::new_field(root, new_table,
                           keep_type);

    if (res)
        res->length_bytes = length_bytes;

    return res;
}


Field *Field_varstring::new_key_field(MEM_ROOT *root,
                                      TABLE *new_table,
                                      uchar *new_ptr, uchar *new_null_ptr,
                                      uint new_null_bit)
{
    Field_varstring *res;

    if ((res = (Field_varstring *) Field::new_key_field(root,
               new_table,
               new_ptr,
               new_null_ptr,
               new_null_bit))) {
        /* Keys length prefixes are always packed with 2 bytes */
        res->length_bytes = 2;
    }

    return res;
}


uint Field_varstring::is_equal(Create_field *new_field)
{
    if (new_field->sql_type == real_type() &&
            new_field->charset == field_charset) {
        if (new_field->length == max_display_length())
            return IS_EQUAL_YES;

        if (new_field->length > max_display_length() &&
                ((new_field->length <= 255 && max_display_length() <= 255) ||
                 (new_field->length > 255 && max_display_length() > 255)))
            return IS_EQUAL_PACK_LENGTH; // VARCHAR, longer variable length
    }

    return IS_EQUAL_NO;
}


void Field_varstring::hash(ulong *nr, ulong *nr2)
{
    if (is_null())
        *nr ^= (*nr << 1) | 1;

    else {
        uint len =  length_bytes == 1 ? (uint) * ptr : uint2korr(ptr);
        const CHARSET_INFO *cs = charset();
        cs->coll->hash_sort(cs, ptr + length_bytes, len, nr, nr2);
    }
}


/****************************************************************************
** blob type
** A blob is saved as a length and a pointer. The length is stored in the
** packlength slot and may be from 1-4.
****************************************************************************/

Field_blob::Field_blob(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
                       enum utype unireg_check_arg, const char *field_name_arg,
                       TABLE_SHARE *share, uint blob_pack_length,
                       const CHARSET_INFO *cs)
    : Field_longstr(ptr_arg, BLOB_PACK_LENGTH_TO_MAX_LENGH(blob_pack_length),
                    null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg,
                    cs),
      packlength(blob_pack_length)
{
    DBUG_ASSERT(blob_pack_length <= 4); // Only pack lengths 1-4 supported currently
    flags |= BLOB_FLAG;
//   share->blob_fields++;
    /* TODO: why do not fill table->s->blob_field array here? */
}


void Field_blob::store_length(uchar *i_ptr,
                              uint i_packlength,
                              uint32 i_number,
                              bool low_byte_first)
{
    switch (i_packlength) {
    case 1:
        i_ptr[0] = (uchar) i_number;
        break;

    case 2:
#ifdef WORDS_BIGENDIAN
        if (low_byte_first)
            int2store(i_ptr, (unsigned short) i_number);

        else
#endif
            shortstore(i_ptr, (unsigned short) i_number);

        break;

    case 3:
        int3store(i_ptr, i_number);
        break;

    case 4:
#ifdef WORDS_BIGENDIAN
        if (low_byte_first)
            int4store(i_ptr, i_number);

        else
#endif
            longstore(i_ptr, i_number);
    }
}


uint32 Field_blob::get_length(const uchar *pos, uint packlength_arg, bool low_byte_first)
{
    switch (packlength_arg) {
    case 1:
        return (uint32) pos[0];

    case 2: {
        uint16 tmp;
#ifdef WORDS_BIGENDIAN

        if (low_byte_first)
            tmp = sint2korr(pos);
        else
#endif
            shortget(tmp, pos);

        return (uint32) tmp;
    }

    case 3:
        return (uint32) uint3korr(pos);

    case 4: {
        uint32 tmp;
#ifdef WORDS_BIGENDIAN

        if (low_byte_first)
            tmp = uint4korr(pos);
        else
#endif
            longget(tmp, pos);

        return (uint32) tmp;
    }
    }

    /* When expanding this, see also MAX_FIELD_BLOBLENGTH. */
    return 0;					// Impossible
}


/**
  Put a blob length field into a record buffer.

  Depending on the maximum length of a blob, its length field is
  put into 1 to 4 bytes. This is a property of the blob object,
  described by 'packlength'.

  @param pos                 Pointer into the record buffer.
  @param length              The length value to put.
*/

void Field_blob::put_length(uchar *pos, uint32 length)
{
    switch (packlength) {
    case 1:
        *pos = (char) length;
        break;

    case 2:
        int2store(pos, length);
        break;

    case 3:
        int3store(pos, length);
        break;

    case 4:
        int4store(pos, length);
        break;
    }
}


/**
  Store a blob value to memory storage.
  @param     from         - the string value to store.
  @param     length       - length of the string value.
  @param     cs           - character set of the string value.
  @param     max_length   - Cut at this length safely (multibyte aware).
  @param OUT blob_storage - Memory storage to put value to.
*/
type_conversion_status Field_blob::store_to_mem(const char *from, uint length,
        const CHARSET_INFO *cs,
        uint max_length,
        Blob_mem_storage *blob_storage)
{
    DBUG_ASSERT(length > 0);
    /*
      We don't need to support escaping or character set conversions here,
      because store_to_mem() is currently called only when we process
      queries having GROUP_CONCAT with ORDER BY or DISTINCT,
      hence some assersions:
    */
    DBUG_ASSERT(!f_is_hex_escape(flags));
    DBUG_ASSERT(field_charset == cs);
    DBUG_ASSERT(length <= max_data_length());

    if (length > max_length) {
        int well_formed_error;
        length = cs->cset->well_formed_len(cs, from, from + max_length,
                                           length, &well_formed_error);
        table->blob_storage->set_truncated_value(true);
    }

    char *tmp;

    if (!(tmp = table->blob_storage->store(from, length))) {
        memset(ptr, 0, Field_blob::pack_length());
        return TYPE_ERR_OOM;
    }

    store_ptr_and_length(tmp, length);
    return TYPE_OK;
}


type_conversion_status Field_blob::store_internal(const char *from, uint length,
        const CHARSET_INFO *cs)
{
    uint new_length;
    char buff[STRING_BUFFER_USUAL_SIZE], *tmp;
    String tmpstr(buff, sizeof(buff), &my_charset_bin);
    DBUG_ASSERT(length > 0);

    /*
      If the 'from' address is in the range of the temporary 'value'-
      object we need to copy the content to a different location or it will be
      invalidated when the 'value'-object is reallocated to make room for
      the new character set.
    */
    if (from >= value.ptr() && from <= value.ptr() + value.length()) {
        /*
          If content of the 'from'-address is cached in the 'value'-object
          it is possible that the content needs a character conversion.
        */
        uint32 dummy_offset;

        if (!String::needs_conversion(length, cs, field_charset, &dummy_offset)) {
            store_ptr_and_length(from, length);
            return TYPE_OK;
        }

        if (tmpstr.copy(from, length, cs))
            goto oom_error;

        from = tmpstr.ptr();
    }

    new_length = min(max_data_length(), field_charset->mbmaxlen * length);

    if (value.alloc(new_length))
        goto oom_error;

    tmp = const_cast<char *>(value.ptr());

    if (f_is_hex_escape(flags)) {
        uint copy_length = my_copy_with_hex_escaping(field_charset,
                           tmp, new_length,
                           from, length);
        store_ptr_and_length(tmp, copy_length);
        return TYPE_OK;
    }

    {
        const char *well_formed_error_pos;
        const char *cannot_convert_error_pos;
        const char *from_end_pos;
        /*
          "length" is OK as "nchars" argument to well_formed_copy_nchars as this
          is never used to limit the length of the data. The cut of long data
          is done with the new_length value.
        */
        uint copy_length = well_formed_copy_nchars(field_charset,
                           tmp, new_length,
                           cs, from, length,
                           length,
                           &well_formed_error_pos,
                           &cannot_convert_error_pos,
                           &from_end_pos);
        store_ptr_and_length(tmp, copy_length);
        return check_string_copy_error(well_formed_error_pos,
                                       cannot_convert_error_pos, from_end_pos,
                                       from + length, true, cs);
    }

oom_error:
    /* Fatal OOM error */
    memset(ptr, 0, Field_blob::pack_length());
    return TYPE_ERR_OOM;
}


type_conversion_status Field_blob::store(const char *from, uint length, const CHARSET_INFO *cs)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;

    if (!length) {
        memset(ptr, 0, Field_blob::pack_length());
        return TYPE_OK;
    }

    if (table->blob_storage)    // GROUP_CONCAT with ORDER BY | DISTINCT
        return store_to_mem(from, length, cs,
                            table->in_use->variables.group_concat_max_len,
                            table->blob_storage);

    return store_internal(from, length, cs);
}


type_conversion_status Field_blob::store(double nr)
{
    const CHARSET_INFO *cs = charset();
    value.set_real(nr, NOT_FIXED_DEC, cs);
    return Field_blob::store(value.ptr(), (uint) value.length(), cs);
}


type_conversion_status Field_blob::store(longlong nr, bool unsigned_val)
{
    const CHARSET_INFO *cs = charset();
    value.set_int(nr, unsigned_val, cs);
    return Field_blob::store(value.ptr(), (uint) value.length(), cs);
}


double Field_blob::val_real(void)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    int not_used;
    char *end_not_used, *blob;
    uint32 length;
    const CHARSET_INFO *cs;
    memcpy(&blob, ptr + packlength, sizeof(char *));

    if (!blob)
        return 0.0;

    length = get_length(ptr);
    cs = charset();
    return my_strntod(cs, blob, length, &end_not_used, &not_used);
}


longlong Field_blob::val_int(void)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    int not_used;
    char *blob;
    memcpy(&blob, ptr + packlength, sizeof(char *));

    if (!blob)
        return 0;

    uint32 length = get_length(ptr);
    return my_strntoll(charset(), blob, length, 10, NULL, &not_used);
}

String *Field_blob::val_str(String *val_buffer __attribute__((unused)),
                            String *val_ptr)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    char *blob;
    memcpy(&blob, ptr + packlength, sizeof(char *));

    if (!blob)
        val_ptr->set("", 0, charset());	// A bit safer than ->length(0)
    else
        val_ptr->set((const char *) blob, get_length(ptr), charset());

    return val_ptr;
}


my_decimal *Field_blob::val_decimal(my_decimal *decimal_value)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    const char *blob;
    size_t length;
    memcpy(&blob, ptr + packlength, sizeof(const uchar *));

    if (!blob) {
        blob = "";
        length = 0;

    } else
        length = get_length(ptr);

    str2my_decimal(E_DEC_FATAL_ERROR, blob, length, charset(),
                   decimal_value);
    return decimal_value;
}


int Field_blob::cmp(const uchar *a, uint32 a_length, const uchar *b,
                    uint32 b_length)
{
    return field_charset->coll->strnncollsp(field_charset,
                                            a, a_length, b, b_length,
                                            0);
}


int Field_blob::cmp_max(const uchar *a_ptr, const uchar *b_ptr,
                        uint max_length)
{
    uchar *blob1, *blob2;
    memcpy(&blob1, a_ptr + packlength, sizeof(char *));
    memcpy(&blob2, b_ptr + packlength, sizeof(char *));
    uint a_len = get_length(a_ptr), b_len = get_length(b_ptr);
    set_if_smaller(a_len, max_length);
    set_if_smaller(b_len, max_length);
    return Field_blob::cmp(blob1, a_len, blob2, b_len);
}


int Field_blob::cmp_binary(const uchar *a_ptr, const uchar *b_ptr,
                           uint32 max_length)
{
    char *a, *b;
    uint diff;
    uint32 a_length, b_length;
    memcpy(&a, a_ptr + packlength, sizeof(char *));
    memcpy(&b, b_ptr + packlength, sizeof(char *));
    a_length = get_length(a_ptr);

    if (a_length > max_length)
        a_length = max_length;

    b_length = get_length(b_ptr);

    if (b_length > max_length)
        b_length = max_length;

    diff = memcmp(a, b, min(a_length, b_length));
    return diff ? diff : (int) (a_length - b_length);
}


/* The following is used only when comparing a key */

uint Field_blob::get_key_image(uchar *buff, uint length, imagetype type_arg)
{
    uint32 blob_length = get_length(ptr);
    uchar *blob;
// #ifdef HAVE_SPATIAL
//   if (type_arg == itMBR)
//   {
//     const char *dummy;
//     MBR mbr;
//     Geometry_buffer buffer;
//     Geometry *gobj;
//     const uint image_length= SIZEOF_STORED_DOUBLE*4;
//
//     if (blob_length < SRID_SIZE)
//     {
//       memset(buff, 0, image_length);
//       return image_length;
//     }
//     get_ptr(&blob);
//     gobj= Geometry::construct(&buffer, (char*) blob, blob_length);
//     if (!gobj || gobj->get_mbr(&mbr, &dummy))
//       memset(buff, 0, image_length);
//     else
//     {
//       float8store(buff,    mbr.xmin);
//       float8store(buff+8,  mbr.xmax);
//       float8store(buff+16, mbr.ymin);
//       float8store(buff+24, mbr.ymax);
//     }
//     return image_length;
//   }
// #endif /*HAVE_SPATIAL*/
    get_ptr(&blob);
    uint local_char_length = length / field_charset->mbmaxlen;
    local_char_length = my_charpos(field_charset, blob, blob + blob_length,
                                   local_char_length);
    set_if_smaller(blob_length, local_char_length);

    if ((uint32) length > blob_length) {
        /*
          Must clear this as we do a memcmp in opt_range.cc to detect
          identical keys
        */
        memset(buff + HA_KEY_BLOB_LENGTH + blob_length, 0, (length - blob_length));
        length = (uint) blob_length;
    }

    int2store(buff, length);
    memcpy(buff + HA_KEY_BLOB_LENGTH, blob, length);
    return HA_KEY_BLOB_LENGTH + length;
}


void Field_blob::set_key_image(const uchar *buff, uint length)
{
    length = uint2korr(buff);
    (void) Field_blob::store((const char *) buff + HA_KEY_BLOB_LENGTH, length,
                             field_charset);
}


int Field_blob::key_cmp(const uchar *key_ptr, uint max_key_length)
{
    uchar *blob1;
    uint blob_length = get_length(ptr);
    memcpy(&blob1, ptr + packlength, sizeof(char *));
    const CHARSET_INFO *cs = charset();
    uint local_char_length = max_key_length / cs->mbmaxlen;
    local_char_length = my_charpos(cs, blob1, blob1 + blob_length,
                                   local_char_length);
    set_if_smaller(blob_length, local_char_length);
    return Field_blob::cmp(blob1, blob_length,
                           key_ptr + HA_KEY_BLOB_LENGTH,
                           uint2korr(key_ptr));
}

int Field_blob::key_cmp(const uchar *a, const uchar *b)
{
    return Field_blob::cmp(a + HA_KEY_BLOB_LENGTH, uint2korr(a),
                           b + HA_KEY_BLOB_LENGTH, uint2korr(b));
}


/**
   Save the field metadata for blob fields.

   Saves the pack length in the first byte of the field metadata array
   at index of *metadata_ptr.

   @param   metadata_ptr   First byte of field metadata

   @returns number of bytes written to metadata_ptr
*/
int Field_blob::do_save_field_metadata(uchar *metadata_ptr)
{
    DBUG_ENTER("Field_blob::do_save_field_metadata");
    *metadata_ptr = pack_length_no_ptr();
    DBUG_PRINT("debug", ("metadata: %u (pack_length_no_ptr)", *metadata_ptr));
    DBUG_RETURN(1);
}


uint32 Field_blob::sort_length() const
{
    return (uint32) (current_thd->variables.max_sort_length +
                     (field_charset == &my_charset_bin ? 0 : packlength));
}


void Field_blob::make_sort_key(uchar *to, uint length)
{
    uchar *blob;
    uint blob_length = get_length();

    if (!blob_length)
        memset(to, 0, length);
    else {
        if (field_charset == &my_charset_bin) {
            uchar *pos;
            /*
              Store length of blob last in blob to shorter blobs before longer blobs
            */
            length -= packlength;
            pos = to + length;

            switch (packlength) {
            case 1:
                *pos = (char) blob_length;
                break;

            case 2:
                mi_int2store(pos, blob_length);
                break;

            case 3:
                mi_int3store(pos, blob_length);
                break;

            case 4:
                mi_int4store(pos, blob_length);
                break;
            }
        }

        memcpy(&blob, ptr + packlength, sizeof(char *));
        blob_length = field_charset->coll->strnxfrm(field_charset,
                      to, length, length,
                      blob, blob_length,
                      MY_STRXFRM_PAD_WITH_SPACE |
                      MY_STRXFRM_PAD_TO_MAXLEN);
        DBUG_ASSERT(blob_length == length);
    }
}


void Field_blob::sql_type(String &res) const
{
    const char *str;
    uint length;

    switch (packlength) {
    default:
        str = "tiny";
        length = 4;
        break;

    case 2:
        str = "";
        length = 0;
        break;

    case 3:
        str = "medium";
        length = 6;
        break;

    case 4:
        str = "long";
        length = 4;
        break;
    }

    res.set_ascii(str, length);

    if (charset() == &my_charset_bin)
        res.append(STRING_WITH_LEN("blob"));
    else
        res.append(STRING_WITH_LEN("text"));
}

uchar *Field_blob::pack(uchar *to, const uchar *from,
                        uint max_length, bool low_byte_first)
{
    uchar *save = ptr;
    ptr = (uchar *) from;
    uint32 length = get_length();			// Length of from string
    /*
      Store max length, which will occupy packlength bytes. If the max
      length given is smaller than the actual length of the blob, we
      just store the initial bytes of the blob.
    */
    store_length(to, packlength, min(length, max_length), low_byte_first);

    /*
      Store the actual blob data, which will occupy 'length' bytes.
     */
    if (length > 0) {
        get_ptr((uchar **) &from);
        memcpy(to + packlength, from, length);
    }

    ptr = save;					// Restore org row pointer
    return to + packlength + length;
}


/**
   Unpack a blob field from row data.

   This method is used to unpack a blob field from a master whose size of
   the field is less than that of the slave. Note: This method is included
   to satisfy inheritance rules, but is not needed for blob fields. It
   simply is used as a pass-through to the original unpack() method for
   blob fields.

   @param   to         Destination of the data
   @param   from       Source of the data
   @param   param_data @c TRUE if base types should be stored in little-
                       endian format, @c FALSE if native format should
                       be used.

   @return  New pointer into memory based on from + length of the data
*/
const uchar *Field_blob::unpack(uchar *to,
                                const uchar *from,
                                uint param_data,
                                bool low_byte_first)
{
    DBUG_ENTER("Field_blob::unpack");
    DBUG_PRINT("enter", ("to: 0x%lx; from: 0x%lx;"
                         " param_data: %u; low_byte_first: %d",
                         (ulong) to, (ulong) from, param_data, low_byte_first));
    uint const master_packlength =
        param_data > 0 ? param_data & 0xFF : packlength;
    uint32 const length = get_length(from, master_packlength, low_byte_first);
    DBUG_DUMP("packed", from, length + master_packlength);
    bitmap_set_bit(table->write_set, field_index);
    store(reinterpret_cast<const char *>(from) + master_packlength,
          length, field_charset);
#ifndef DBUG_OFF
    uchar *vptr;
    get_ptr(&vptr);
    DBUG_DUMP("field", ptr, pack_length() /* len bytes + ptr bytes */);
    DBUG_DUMP("value", vptr, length /* the blob value length */);
#endif
    DBUG_RETURN(from + master_packlength + length);
}

uint Field_blob::packed_col_length(const uchar *data_ptr, uint length)
{
    if (length > 255)
        return uint2korr(data_ptr) + 2;

    return (uint) * data_ptr + 1;
}


uint Field_blob::max_packed_col_length(uint max_length)
{
    return (max_length > 255 ? 2 : 1) + max_length;
}


uint Field_blob::is_equal(Create_field *new_field)
{
    if (field_flags_are_binary() != new_field->field_flags_are_binary())
        return 0;

    return ((new_field->sql_type == get_blob_type_from_length(max_data_length()))
            && new_field->charset == field_charset &&
            new_field->pack_length == pack_length());
}


#ifdef HAVE_SPATIAL

void Field_geom::sql_type(String &res) const
{
    const CHARSET_INFO *cs = &my_charset_latin1;

    switch (geom_type) {
    case GEOM_POINT:
        res.set(STRING_WITH_LEN("point"), cs);
        break;

    case GEOM_LINESTRING:
        res.set(STRING_WITH_LEN("linestring"), cs);
        break;

    case GEOM_POLYGON:
        res.set(STRING_WITH_LEN("polygon"), cs);
        break;

    case GEOM_MULTIPOINT:
        res.set(STRING_WITH_LEN("multipoint"), cs);
        break;

    case GEOM_MULTILINESTRING:
        res.set(STRING_WITH_LEN("multilinestring"), cs);
        break;

    case GEOM_MULTIPOLYGON:
        res.set(STRING_WITH_LEN("multipolygon"), cs);
        break;

    case GEOM_GEOMETRYCOLLECTION:
        res.set(STRING_WITH_LEN("geometrycollection"), cs);
        break;

    default:
        res.set(STRING_WITH_LEN("geometry"), cs);
    }
}


type_conversion_status Field_geom::store(double nr)
{
    my_message(ER_CANT_CREATE_GEOMETRY_OBJECT,
               ER(ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0));
    return TYPE_ERR_BAD_VALUE;
}


type_conversion_status Field_geom::store(longlong nr, bool unsigned_val)
{
    my_message(ER_CANT_CREATE_GEOMETRY_OBJECT,
               ER(ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0));
    return TYPE_ERR_BAD_VALUE;
}


type_conversion_status Field_geom::store_decimal(const my_decimal *)
{
    my_message(ER_CANT_CREATE_GEOMETRY_OBJECT,
               ER(ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0));
    return TYPE_ERR_BAD_VALUE;
}


type_conversion_status Field_geom::store_internal(const char *from, uint length,
        const CHARSET_INFO *cs)
{
    uint32 wkb_type;
    DBUG_ASSERT(length > 0);
//   // Check given WKB
//   if (from == Geometry::bad_geometry_data.ptr() ||
//       length < SRID_SIZE + WKB_HEADER_SIZE + SIZEOF_STORED_DOUBLE * 2 ||
//       (wkb_type= uint4korr(from + SRID_SIZE + 1)) < (uint32) Geometry::wkb_point ||
//        wkb_type > (uint32) Geometry::wkb_last)
//   {
//     memset(ptr, 0, Field_blob::pack_length());
//     my_message(ER_CANT_CREATE_GEOMETRY_OBJECT,
//                ER(ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0));
//     return TYPE_ERR_BAD_VALUE;
//   }
//
//   if (table->copy_blobs || length <= MAX_FIELD_WIDTH)
//   {                                                   // Must make a copy
//     value.copy(from, length, cs);
//     from= value.ptr();
//   }
//
//   store_ptr_and_length(from, length);
    return TYPE_OK;
}


uint Field_geom::is_equal(Create_field *new_field)
{
    return new_field->field_flags_are_binary() == field_flags_are_binary() &&
           new_field->sql_type == real_type() &&
           new_field->geom_type == get_geometry_type() &&
           new_field->charset == field_charset &&
           new_field->pack_length == pack_length();
}


#endif /*HAVE_SPATIAL*/

/****************************************************************************
** enum type.
** This is a string which only can have a selection of different values.
** If one uses this string in a number context one gets the type number.
****************************************************************************/

enum ha_base_keytype Field_enum::key_type() const
{
    switch (packlength) {
    default:
                return HA_KEYTYPE_BINARY;

    case 2:
        return HA_KEYTYPE_USHORT_INT;

    case 3:
        return HA_KEYTYPE_UINT24;

    case 4:
        return HA_KEYTYPE_ULONG_INT;

    case 8:
        return HA_KEYTYPE_ULONGLONG;
    }
}

void Field_enum::store_type(ulonglong value)
{
    switch (packlength) {
    case 1:
        ptr[0] = (uchar) value;
        break;

    case 2:
#ifdef WORDS_BIGENDIAN
        if (table->s->db_low_byte_first)
            int2store(ptr, (unsigned short) value);

        else
#endif
            shortstore(ptr, (unsigned short) value);

        break;

    case 3:
        int3store(ptr, (long) value);
        break;

    case 4:
#ifdef WORDS_BIGENDIAN
        if (table->s->db_low_byte_first)
            int4store(ptr, value);

        else
#endif
            longstore(ptr, (long) value);

        break;

    case 8:
#ifdef WORDS_BIGENDIAN
        if (table->s->db_low_byte_first)
            int8store(ptr, value);

        else
#endif
            longlongstore(ptr, value);

        break;
    }
}


/**
  @note
    Storing a empty string in a enum field gives a warning
    (if there isn't a empty value in the enum)
*/

type_conversion_status Field_enum::store(const char *from, uint length, const CHARSET_INFO *cs)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    int err = 0;
    type_conversion_status ret = TYPE_OK;
    uint32 not_used;
    char buff[STRING_BUFFER_USUAL_SIZE];
    String tmpstr(buff, sizeof(buff), &my_charset_bin);

    /* Convert character set if necessary */
    if (String::needs_conversion(length, cs, field_charset, &not_used)) {
        uint dummy_errors;
        tmpstr.copy(from, length, cs, field_charset, &dummy_errors);
        from = tmpstr.ptr();
        length =  tmpstr.length();
    }

    /* Remove end space */
    length = field_charset->cset->lengthsp(field_charset, from, length);
    uint tmp = find_type2(typelib, from, length, field_charset);

    if (!tmp) {
        if (length < 6) { // Can't be more than 99999 enums
            /* This is for reading numbers with LOAD DATA INFILE */
            char *end;
            tmp = (uint) my_strntoul(cs, from, length, 10, &end, &err);

            if (err || end != from + length || tmp > typelib->count) {
                tmp = 0;
                set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
                ret = TYPE_WARN_TRUNCATED;
            }

            if (!table->in_use->count_cuted_fields)
                ret = TYPE_OK;

        } else
            set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
    }

    store_type((ulonglong) tmp);
    return ret;
}


type_conversion_status Field_enum::store(double nr)
{
    return Field_enum::store((longlong) nr, FALSE);
}


type_conversion_status Field_enum::store(longlong nr, bool unsigned_val)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    type_conversion_status error = TYPE_OK;

    if ((ulonglong) nr > typelib->count || nr == 0) {
        set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);

        if (nr != 0 || table->in_use->count_cuted_fields) {
            nr = 0;
            error = TYPE_WARN_TRUNCATED;
        }
    }

    store_type((ulonglong) (uint) nr);
    return error;
}


double Field_enum::val_real(void)
{
    return (double) Field_enum::val_int();
}


my_decimal *Field_enum::val_decimal(my_decimal *decimal_value)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    int2my_decimal(E_DEC_FATAL_ERROR, val_int(), 0, decimal_value);
    return decimal_value;
}


longlong Field_enum::val_int(void)
{
    ASSERT_COLUMN_MARKED_FOR_READ;

    switch (packlength) {
    case 1:
        return (longlong) ptr[0];

    case 2: {
        uint16 tmp;
#ifdef WORDS_BIGENDIAN

        if (table->s->db_low_byte_first)
            tmp = sint2korr(ptr);
        else
#endif
            shortget(tmp, ptr);

        return (longlong) tmp;
    }

    case 3:
        return (longlong) uint3korr(ptr);

    case 4: {
        uint32 tmp;
#ifdef WORDS_BIGENDIAN

        if (table->s->db_low_byte_first)
            tmp = uint4korr(ptr);
        else
#endif
            longget(tmp, ptr);

        return (longlong) tmp;
    }

    case 8: {
        longlong tmp;
#ifdef WORDS_BIGENDIAN

        if (table->s->db_low_byte_first)
            tmp = sint8korr(ptr);
        else
#endif
            longlongget(tmp, ptr);

        return tmp;
    }
    }

    return 0;					// impossible
}


/**
   Save the field metadata for enum fields.

   Saves the real type in the first byte and the pack length in the
   second byte of the field metadata array at index of *metadata_ptr and
   *(metadata_ptr + 1).

   @param   metadata_ptr   First byte of field metadata

   @returns number of bytes written to metadata_ptr
*/
int Field_enum::do_save_field_metadata(uchar *metadata_ptr)
{
    *metadata_ptr = real_type();
    *(metadata_ptr + 1) = pack_length();
    return 2;
}


String *Field_enum::val_str(String *val_buffer __attribute__((unused)),
                            String *val_ptr)
{
    char val[32];
    uint tmp = (uint) Field_enum::val_int();
    sprintf(val, "%d", tmp);
    val_ptr->set(val, strlen(val), field_charset);
    return val_ptr;
}

int Field_enum::cmp(const uchar *a_ptr, const uchar *b_ptr)
{
    uchar *old = ptr;
    ptr = (uchar *) a_ptr;
    ulonglong a = Field_enum::val_int();
    ptr = (uchar *) b_ptr;
    ulonglong b = Field_enum::val_int();
    ptr = old;
    return (a < b) ? -1 : (a > b) ? 1 : 0;
}

void Field_enum::make_sort_key(uchar *to, uint length)
{
#ifdef WORDS_BIGENDIAN

    if (!table->s->db_low_byte_first)
        copy_integer<true>(to, length, ptr, packlength, true);
    else
#endif
        copy_integer<false>(to, length, ptr, packlength, true);
}


void Field_enum::sql_type(String &res) const
{
    char buffer[255];
    String enum_item(buffer, sizeof(buffer), res.charset());
    res.length(0);
    res.append(STRING_WITH_LEN("enum("));
    bool flag = 0;
    uint *len = typelib->type_lengths;

    for (const char **pos = typelib->type_names; *pos; pos++, len++) {
        uint dummy_errors;

        if (flag)
            res.append(',');

        /* convert to res.charset() == utf8, then quote */
        enum_item.copy(*pos, *len, charset(), res.charset(), &dummy_errors);
        append_unescaped(&res, enum_item.ptr(), enum_item.length());
        flag = 1;
    }

    res.append(')');
}


Field *Field_enum::new_field(MEM_ROOT *root, TABLE *new_table,
                             bool keep_type)
{
    Field_enum *res = (Field_enum *) Field::new_field(root, new_table, keep_type);

    if (res)
        res->typelib = copy_typelib(root, typelib);

    return res;
}


/*
   set type.
   This is a string which can have a collection of different values.
   Each string value is separated with a ','.
   For example "One,two,five"
   If one uses this string in a number context one gets the bits as a longlong
   number.
*/


type_conversion_status Field_set::store(const char *from, uint length, const CHARSET_INFO *cs)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    bool got_warning = 0;
    int err = 0;
    type_conversion_status ret = TYPE_OK;
    char *not_used;
    uint not_used2;
    uint32 not_used_offset;
    char buff[STRING_BUFFER_USUAL_SIZE];
    String tmpstr(buff, sizeof(buff), &my_charset_bin);

    /* Convert character set if necessary */
    if (String::needs_conversion(length, cs, field_charset, &not_used_offset)) {
        uint dummy_errors;
        tmpstr.copy(from, length, cs, field_charset, &dummy_errors);
        from = tmpstr.ptr();
        length =  tmpstr.length();
    }

    ulonglong tmp = find_set(typelib, from, length, field_charset,
                             &not_used, &not_used2, &got_warning);

    if (!tmp && length && length < 22) {
        /* This is for reading numbers with LOAD DATA INFILE */
        char *end;
        tmp = my_strntoull(cs, from, length, 10, &end, &err);

        if (err || end != from + length ||
                tmp > (ulonglong) (((longlong) 1 << typelib->count) - (longlong) 1)) {
            tmp = 0;
            set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
            ret = TYPE_WARN_TRUNCATED;
        }

    } else if (got_warning)
        set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);

    store_type(tmp);
    return ret;
}


type_conversion_status Field_set::store(longlong nr, bool unsigned_val)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    type_conversion_status error = TYPE_OK;
    ulonglong max_nr;

    if (sizeof(ulonglong) * 8 <= typelib->count)
        max_nr = ULONGLONG_MAX;
    else
        max_nr = (ULL(1) << typelib->count) - 1;

    if ((ulonglong) nr > max_nr) {
        nr &= max_nr;
        set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
        error = TYPE_WARN_TRUNCATED;
    }

    store_type((ulonglong) nr);
    return error;
}


String *Field_set::val_str(String *val_buffer,
                           String *val_ptr __attribute__((unused)))
{
    char val[128];
    ulonglong tmp = (ulonglong) Field_enum::val_int();
    sprintf(val, "%lld", tmp);
    val_ptr->set(val, strlen(val), field_charset);
    return val_ptr;
}


void Field_set::sql_type(String &res) const
{
    char buffer[255];
    String set_item(buffer, sizeof(buffer), res.charset());
    res.length(0);
    res.append(STRING_WITH_LEN("set("));
    bool flag = 0;
    uint *len = typelib->type_lengths;

    for (const char **pos = typelib->type_names; *pos; pos++, len++) {
        uint dummy_errors;

        if (flag)
            res.append(',');

        /* convert to res.charset() == utf8, then quote */
        set_item.copy(*pos, *len, charset(), res.charset(), &dummy_errors);
        append_unescaped(&res, set_item.ptr(), set_item.length());
        flag = 1;
    }

    res.append(')');
}

/**
  @retval
    1  if the fields are equally defined
  @retval
    0  if the fields are unequally defined
*/

bool Field::eq_def(Field *field)
{
    if (real_type() != field->real_type() || charset() != field->charset() ||
            pack_length() != field->pack_length())
        return 0;

    return 1;
}


/**
  Compare the first t1::count type names.

  @return TRUE if the type names of t1 match those of t2. FALSE otherwise.
*/

static bool compare_type_names(const CHARSET_INFO *charset,
                               TYPELIB *t1, TYPELIB *t2)
{
    for (uint i = 0; i < t1->count; i++)
        if (my_strnncoll(charset,
                         (const uchar *) t1->type_names[i],
                         t1->type_lengths[i],
                         (const uchar *) t2->type_names[i],
                         t2->type_lengths[i]))
            return FALSE;

    return TRUE;
}

/**
  @return
  returns 1 if the fields are equally defined
*/

bool Field_enum::eq_def(Field *field)
{
    TYPELIB *values;

    if (!Field::eq_def(field))
        return FALSE;

    values = ((Field_enum *) field)->typelib;

    /* Definition must be strictly equal. */
    if (typelib->count != values->count)
        return FALSE;

    return compare_type_names(field_charset, typelib, values);
}


/**
  Check whether two fields can be considered 'equal' for table
  alteration purposes. Fields are equal if they retain the same
  pack length and if new members are added to the end of the list.

  @return IS_EQUAL_YES if fields are compatible.
          IS_EQUAL_NO otherwise.
*/

uint Field_enum::is_equal(Create_field *new_field)
{
    TYPELIB *values = new_field->interval;

    /*
      The fields are compatible if they have the same flags,
      type, charset and have the same underlying length.
    */
    if (new_field->field_flags_are_binary() != field_flags_are_binary() ||
            new_field->sql_type != real_type() ||
            new_field->charset != field_charset ||
            new_field->pack_length != pack_length())
        return IS_EQUAL_NO;

    /*
      Changing the definition of an ENUM or SET column by adding a new
      enumeration or set members to the end of the list of valid member
      values only alters table metadata and not table data.
    */
    if (typelib->count > values->count)
        return IS_EQUAL_NO;

    /* Check whether there are modification before the end. */
    if (! compare_type_names(field_charset, typelib, new_field->interval))
        return IS_EQUAL_NO;

    return IS_EQUAL_YES;
}


uchar *Field_enum::pack(uchar *to, const uchar *from,
                        uint max_length, bool low_byte_first)
{
    DBUG_ENTER("Field_enum::pack");
    DBUG_PRINT("debug", ("packlength: %d", packlength));
    DBUG_DUMP("from", from, packlength);

    switch (packlength) {
    case 1:
        *to = *from;
        DBUG_RETURN(to + 1);

    case 2:
        DBUG_RETURN(pack_int16(to, from, low_byte_first));

    case 3:
        DBUG_RETURN(pack_int24(to, from, low_byte_first));

    case 4:
        DBUG_RETURN(pack_int32(to, from, low_byte_first));

    case 8:
        DBUG_RETURN(pack_int64(to, from, low_byte_first));

    default:
        DBUG_ASSERT(0);
    }

    MY_ASSERT_UNREACHABLE();
    DBUG_RETURN(NULL);
}

const uchar *Field_enum::unpack(uchar *to, const uchar *from,
                                uint param_data, bool low_byte_first)
{
    DBUG_ENTER("Field_enum::unpack");
    DBUG_PRINT("debug", ("packlength: %d", packlength));
    DBUG_DUMP("from", from, packlength);

    switch (packlength) {
    case 1:
        *to = *from;
        DBUG_RETURN(from + 1);

    case 2:
        DBUG_RETURN(unpack_int16(to, from, low_byte_first));

    case 3:
        DBUG_RETURN(unpack_int24(to, from, low_byte_first));

    case 4:
        DBUG_RETURN(unpack_int32(to, from, low_byte_first));

    case 8:
        DBUG_RETURN(unpack_int64(to, from, low_byte_first));

    default:
        DBUG_ASSERT(0);
    }

    MY_ASSERT_UNREACHABLE();
    DBUG_RETURN(NULL);
}


/**
  @return
  returns 1 if the fields are equally defined
*/
bool Field_num::eq_def(Field *field)
{
    if (!Field::eq_def(field))
        return 0;

    Field_num *from_num = (Field_num *) field;

    if (unsigned_flag != from_num->unsigned_flag ||
            (zerofill && !from_num->zerofill && !zero_pack()) ||
            dec != from_num->dec)
        return 0;

    return 1;
}


/**
  Check whether two numeric fields can be considered 'equal' for table
  alteration purposes. Fields are equal if they are of the same type
  and retain the same pack length.
*/

uint Field_num::is_equal(Create_field *new_field)
{
    return ((new_field->sql_type == real_type()) &&
            ((new_field->flags & UNSIGNED_FLAG) ==
             (uint) (flags & UNSIGNED_FLAG)) &&
            ((new_field->flags & AUTO_INCREMENT_FLAG) ==
             (uint) (flags & AUTO_INCREMENT_FLAG)) &&
            (new_field->pack_length == pack_length()));
}


/*
  Bit field.

  We store the first 0 - 6 uneven bits among the null bits
  at the start of the record. The rest bytes are stored in
  the record itself.

  For example:

  CREATE TABLE t1 (a int, b bit(17), c bit(21) not null, d bit(8));
  We would store data  as follows in the record:

  Byte        Bit
  1           7 - reserve for delete
              6 - null bit for 'a'
              5 - null bit for 'b'
              4 - first (high) bit of 'b'
              3 - first (high) bit of 'c'
              2 - second bit of 'c'
              1 - third bit of 'c'
              0 - forth bit of 'c'
  2           7 - firth bit of 'c'
              6 - null bit for 'd'
  3 - 6       four bytes for 'a'
  7 - 8       two bytes for 'b'
  9 - 10      two bytes for 'c'
  11          one byte for 'd'
*/

Field_bit::Field_bit(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
                     uchar null_bit_arg, uchar *bit_ptr_arg, uchar bit_ofs_arg,
                     enum utype unireg_check_arg, const char *field_name_arg)
    : Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
            unireg_check_arg, field_name_arg),
      bit_ptr(bit_ptr_arg), bit_ofs(bit_ofs_arg), bit_len(len_arg & 7),
      bytes_in_rec(len_arg / 8)
{
    DBUG_ENTER("Field_bit::Field_bit");
    DBUG_PRINT("enter", ("ptr_arg: %p, null_ptr_arg: %p, len_arg: %u, bit_len: %u, bytes_in_rec: %u",
                         ptr_arg, null_ptr_arg, len_arg, bit_len, bytes_in_rec));
    flags |= UNSIGNED_FLAG;

    /*
      Ensure that Field::eq() can distinguish between two different bit fields.
      (two bit fields that are not null, may have same ptr and null_ptr)
    */
    if (!null_ptr_arg)
        null_bit = bit_ofs_arg;

    DBUG_VOID_RETURN;
}


void Field_bit::hash(ulong *nr, ulong *nr2)
{
    if (is_null())
        *nr ^= (*nr << 1) | 1;

    else {
        const CHARSET_INFO *cs = &my_charset_bin;
        longlong value = Field_bit::val_int();
        uchar tmp[8];
        mi_int8store(tmp, value);
        cs->coll->hash_sort(cs, tmp, 8, nr, nr2);
    }
}


size_t Field_bit::do_last_null_byte() const
{
    /*
      Code elsewhere is assuming that bytes are 8 bits, so I'm using
      that value instead of the correct one: CHAR_BIT.

      REFACTOR SUGGESTION (Matz): Change to use the correct number of
      bits. On systems with CHAR_BIT > 8 (not very common), the storage
      will lose the extra bits.
    */
    DBUG_PRINT("test", ("bit_ofs: %d, bit_len: %d  bit_ptr: 0x%lx",
                        bit_ofs, bit_len, (long) bit_ptr));
    uchar *result;

    if (bit_len == 0)
        result = null_ptr;
    else if (bit_ofs + bit_len > 8)
        result = bit_ptr + 1;
    else
        result = bit_ptr;

    if (result)
        return (size_t) (result - table->record[0]) + 1;

    return LAST_NULL_BYTE_UNDEF;
}


Field *Field_bit::new_key_field(MEM_ROOT *root,
                                TABLE *new_table,
                                uchar *new_ptr, uchar *new_null_ptr,
                                uint new_null_bit)
{
    Field_bit *res;

    if ((res = (Field_bit *) Field::new_key_field(root, new_table,
               new_ptr, new_null_ptr,
               new_null_bit))) {
        /* Move bits normally stored in null_pointer to new_ptr */
        res->bit_ptr = new_ptr;
        res->bit_ofs = 0;

        if (bit_len)
            res->ptr++;                               // Store rest of data here
    }

    return res;
}


uint Field_bit::is_equal(Create_field *new_field)
{
    return (new_field->sql_type == real_type() &&
            new_field->length == max_display_length());
}


type_conversion_status Field_bit::store(const char *from, uint length, const CHARSET_INFO *cs)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    int delta;

    for (; length && !*from; from++, length--) ;         // skip left 0's

    delta = bytes_in_rec - length;

    /*
      *from should probably be treated like uint here see BUG#13727586
    */
    if (delta < -1 ||
            (delta == -1 && (uchar) *from > ((1 << bit_len) - 1)) ||
            (!bit_len && delta < 0)) {
        set_rec_bits((1 << bit_len) - 1, bit_ptr, bit_ofs, bit_len);
        memset(ptr, 0xff, bytes_in_rec);

        if (table->in_use->really_abort_on_warning())
            set_warning(Sql_condition::WARN_LEVEL_WARN, ER_DATA_TOO_LONG, 1);
        else
            set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);

        return TYPE_WARN_OUT_OF_RANGE;
    }

    /* delta is >= -1 here */
    if (delta > 0) {
        if (bit_len)
            clr_rec_bits(bit_ptr, bit_ofs, bit_len);

        memset(ptr, 0, delta);
        memcpy(ptr + delta, from, length);

    } else if (delta == 0) {
        if (bit_len)
            clr_rec_bits(bit_ptr, bit_ofs, bit_len);

        memcpy(ptr, from, length);

    } else {
        if (bit_len) {
            set_rec_bits((uchar) *from, bit_ptr, bit_ofs, bit_len);
            from++;
        }

        memcpy(ptr, from, bytes_in_rec);
    }

    return TYPE_OK;
}


type_conversion_status Field_bit::store(double nr)
{
    return Field_bit::store((longlong) nr, FALSE);
}


type_conversion_status Field_bit::store(longlong nr, bool unsigned_val)
{
    char buf[8];
    mi_int8store(buf, nr);
    return store(buf, 8, NULL);
}


type_conversion_status Field_bit::store_decimal(const my_decimal *val)
{
    bool has_overflow = false;
    longlong i = convert_decimal2longlong(val, 1, &has_overflow);
    type_conversion_status res = store(i, TRUE);
    return has_overflow ? TYPE_WARN_OUT_OF_RANGE : res;
}


double Field_bit::val_real(void)
{
    return (double) Field_bit::val_int();
}


longlong Field_bit::val_int(void)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    ulonglong bits = 0;

    if (bit_len) {
        bits = get_rec_bits(bit_ptr, bit_ofs, bit_len);
        bits <<= (bytes_in_rec * 8);
    }

    switch (bytes_in_rec) {
    case 0:
        return bits;

    case 1:
        return bits | (ulonglong) ptr[0];

    case 2:
        return bits | mi_uint2korr(ptr);

    case 3:
        return bits | mi_uint3korr(ptr);

    case 4:
        return bits | mi_uint4korr(ptr);

    case 5:
        return bits | mi_uint5korr(ptr);

    case 6:
        return bits | mi_uint6korr(ptr);

    case 7:
        return bits | mi_uint7korr(ptr);

    default:
        return mi_uint8korr(ptr + bytes_in_rec - sizeof(longlong));
    }
}


String *Field_bit::val_str(String *val_buffer,
                           String *val_ptr __attribute__((unused)))
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    char buff[sizeof(longlong)];
    uint length = min<uint>(pack_length(), sizeof(longlong));
    ulonglong bits = val_int();
    mi_int8store(buff, bits);
    val_buffer->alloc(length);
    memcpy((char *) val_buffer->ptr(), buff + 8 - length, length);
    val_buffer->length(length);
    val_buffer->set_charset(&my_charset_bin);
    return val_buffer;
}


my_decimal *Field_bit::val_decimal(my_decimal *deciaml_value)
{
    ASSERT_COLUMN_MARKED_FOR_READ;
    int2my_decimal(E_DEC_FATAL_ERROR, val_int(), 1, deciaml_value);
    return deciaml_value;
}


/*
  Compare two bit fields using pointers within the record.
  SYNOPSIS
    cmp_max()
    a                 Pointer to field->ptr in first record
    b                 Pointer to field->ptr in second record
    max_len           Maximum length used in index
  DESCRIPTION
    This method is used from key_rec_cmp used by merge sorts used
    by partitioned index read and later other similar places.
    The a and b pointer must be pointers to the field in a record
    (not the table->record[0] necessarily)
*/
int Field_bit::cmp_max(const uchar *a, const uchar *b, uint max_len)
{
    my_ptrdiff_t a_diff = a - ptr;
    my_ptrdiff_t b_diff = b - ptr;

    if (bit_len) {
        int flag;
        uchar bits_a = get_rec_bits(bit_ptr + a_diff, bit_ofs, bit_len);
        uchar bits_b = get_rec_bits(bit_ptr + b_diff, bit_ofs, bit_len);

        if ((flag = (int) (bits_a - bits_b)))
            return flag;
    }

    return memcmp(a, b, field_length);
}


int Field_bit::key_cmp(const uchar *str, uint length)
{
    if (bit_len) {
        int flag;
        uchar bits = get_rec_bits(bit_ptr, bit_ofs, bit_len);

        if ((flag = (int) (bits - *str)))
            return flag;

        str++;
        length--;
    }

    return memcmp(ptr, str, length);
}


int Field_bit::cmp_offset(uint row_offset)
{
    if (bit_len) {
        int flag;
        uchar bits_a = get_rec_bits(bit_ptr, bit_ofs, bit_len);
        uchar bits_b = get_rec_bits(bit_ptr + row_offset, bit_ofs, bit_len);

        if ((flag = (int) (bits_a - bits_b)))
            return flag;
    }

    return memcmp(ptr, ptr + row_offset, bytes_in_rec);
}


uint Field_bit::get_key_image(uchar *buff, uint length, imagetype type_arg)
{
    if (bit_len) {
        uchar bits = get_rec_bits(bit_ptr, bit_ofs, bit_len);
        *buff++ = bits;
        length--;
    }

    uint data_length = min(length, bytes_in_rec);
    memcpy(buff, ptr, data_length);
    return data_length + 1;
}


/**
   Save the field metadata for bit fields.

   Saves the bit length in the first byte and bytes in record in the
   second byte of the field metadata array at index of *metadata_ptr and
   *(metadata_ptr + 1).

   @param   metadata_ptr   First byte of field metadata

   @returns number of bytes written to metadata_ptr
*/
int Field_bit::do_save_field_metadata(uchar *metadata_ptr)
{
    DBUG_ENTER("Field_bit::do_save_field_metadata");
    DBUG_PRINT("debug", ("bit_len: %d, bytes_in_rec: %d",
                         bit_len, bytes_in_rec));
    /*
      Since this class and Field_bit_as_char have different ideas of
      what should be stored here, we compute the values of the metadata
      explicitly using the field_length.
     */
    metadata_ptr[0] = field_length % 8;
    metadata_ptr[1] = field_length / 8;
    DBUG_RETURN(2);
}


/**
   Returns the number of bytes field uses in row-based replication
   row packed size.

   This method is used in row-based replication to determine the number
   of bytes that the field consumes in the row record format. This is
   used to skip fields in the master that do not exist on the slave.

   @param   field_metadata   Encoded size in field metadata

   @returns The size of the field based on the field metadata.
*/
uint Field_bit::pack_length_from_metadata(uint field_metadata)
{
    uint const from_len = (field_metadata >> 8U) & 0x00ff;
    uint const from_bit_len = field_metadata & 0x00ff;
    uint const source_size = from_len + ((from_bit_len > 0) ? 1 : 0);
    return (source_size);
}

/**
   Check to see if field size is compatible with destination.

   This method is used in row-based replication to verify that the slave's
   field size is less than or equal to the master's field size. The
   encoded field metadata (from the master or source) is decoded and compared
   to the size of this field (the slave or destination).

   @param   field_metadata   Encoded size in field metadata
   @param   order_var        Pointer to variable where the order
                             between the source field and this field
                             will be returned.

   @return @c true
*/
bool Field_bit::compatible_field_size(uint field_metadata,
                                      Relay_log_info *__attribute__((unused)),
                                      uint16 mflags,
                                      int *order_var)
{
    DBUG_ENTER("Field_bit::compatible_field_size");
    DBUG_ASSERT((field_metadata >> 16) == 0);
    uint from_bit_len = 8 * (field_metadata >> 8) + (field_metadata & 0xff);
    uint to_bit_len = max_display_length();
    DBUG_PRINT("debug", ("from_bit_len: %u, to_bit_len: %u",
                         from_bit_len, to_bit_len));
    /*
      If the bit length exact flag is clear, we are dealing with an old
      master, so we allow some less strict behaviour if replicating by
      moving both bit lengths to an even multiple of 8.

      We do this by computing the number of bytes to store the field
      instead, and then compare the result.
     */
    *order_var = compare(from_bit_len, to_bit_len);
    DBUG_RETURN(TRUE);
}



void Field_bit::sql_type(String &res) const
{
    const CHARSET_INFO *cs = res.charset();
    ulong length = cs->cset->snprintf(cs, (char *) res.ptr(), res.alloced_length(),
                                      "bit(%d)", (int) field_length);
    res.length((uint) length);
}


uchar *Field_bit::pack(uchar *to, const uchar *from, uint max_length,
                       bool low_byte_first __attribute__((unused)))
{
    DBUG_ASSERT(max_length > 0);
    uint length;

    if (bit_len > 0) {
        /*
          We have the following:

          ptr        Points into a field in record R1
          from       Points to a field in a record R2
          bit_ptr    Points to the byte (in the null bytes) that holds the
                     odd bits of R1
          from_bitp  Points to the byte that holds the odd bits of R2

          We have the following:

              ptr - bit_ptr = from - from_bitp

          We want to isolate 'from_bitp', so this gives:

              ptr - bit_ptr - from = - from_bitp
              - ptr + bit_ptr + from = from_bitp
              bit_ptr + from - ptr = from_bitp
         */
        uchar bits = get_rec_bits(bit_ptr + (from - ptr), bit_ofs, bit_len);
        *to++ = bits;
    }

    length = min(bytes_in_rec, max_length - (bit_len > 0));
    memcpy(to, from, length);
    return to + length;
}


/**
   Unpack a bit field from row data.

   This method is used to unpack a bit field from a master whose size
   of the field is less than that of the slave.

   @param   to         Destination of the data
   @param   from       Source of the data
   @param   param_data Bit length (upper) and length (lower) values

   @return  New pointer into memory based on from + length of the data
*/
const uchar *Field_bit::unpack(uchar *to, const uchar *from, uint param_data,
                               bool low_byte_first __attribute__((unused)))
{
    DBUG_ENTER("Field_bit::unpack");
    DBUG_PRINT("enter", ("to: %p, from: %p, param_data: 0x%x",
                         to, from, param_data));
    DBUG_PRINT("debug", ("bit_ptr: %p, bit_len: %u, bit_ofs: %u",
                         bit_ptr, bit_len, bit_ofs));
    uint const from_len = (param_data >> 8U) & 0x00ff;
    uint const from_bit_len = param_data & 0x00ff;
    DBUG_PRINT("debug", ("from_len: %u, from_bit_len: %u",
                         from_len, from_bit_len));

    /*
      If the parameter data is zero (i.e., undefined), or if the master
      and slave have the same sizes, then use the old unpack() method.
    */
    if (param_data == 0 ||
            ((from_bit_len == bit_len) && (from_len == bytes_in_rec))) {
        if (bit_len > 0) {
            /*
              set_rec_bits is a macro, don't put the post-increment in the
              argument since that might cause strange side-effects.

              For the choice of the second argument, see the explanation for
              Field_bit::pack().
            */
            set_rec_bits(*from, bit_ptr + (to - ptr), bit_ofs, bit_len);
            from++;
        }

        memcpy(to, from, bytes_in_rec);
        DBUG_RETURN(from + bytes_in_rec);
    }

    /*
      We are converting a smaller bit field to a larger one here.
      To do that, we first need to construct a raw value for the original
      bit value stored in the from buffer. Then that needs to be converted
      to the larger field then sent to store() for writing to the field.
      Lastly the odd bits need to be masked out if the bytes_in_rec > 0.
      Otherwise stray bits can cause spurious values.
    */
    uint new_len = (field_length + 7) / 8;
    char *value = (char *)my_alloca(new_len);
    memset(value, 0, new_len);
    uint len = from_len + ((from_bit_len > 0) ? 1 : 0);
    memcpy(value + (new_len - len), from, len);

    /*
      Mask out the unused bits in the partial byte.
      TODO: Add code to the master to always mask these bits and remove
            the following.
    */
    if ((from_bit_len > 0) && (from_len > 0))
        value[new_len - len] = value[new_len - len] & ((1U << from_bit_len) - 1);

    bitmap_set_bit(table->write_set, field_index);
    store(value, new_len, system_charset_info);
    my_afree(value);
    DBUG_RETURN(from + len);
}


void Field_bit::set_default()
{
    if (bit_len > 0) {
        my_ptrdiff_t const offset = table->s->default_values - table->record[0];
        uchar bits = get_rec_bits(bit_ptr + offset, bit_ofs, bit_len);
        set_rec_bits(bits, bit_ptr, bit_ofs, bit_len);
    }

    Field::set_default();
}

/*
  Bit field support for non-MyISAM tables.
*/

Field_bit_as_char::Field_bit_as_char(uchar *ptr_arg, uint32 len_arg,
                                     uchar *null_ptr_arg, uchar null_bit_arg,
                                     enum utype unireg_check_arg,
                                     const char *field_name_arg)
    : Field_bit(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, 0, 0,
                unireg_check_arg, field_name_arg)
{
    flags |= UNSIGNED_FLAG;
    bit_len = 0;
    bytes_in_rec = (len_arg + 7) / 8;
}


type_conversion_status Field_bit_as_char::store(const char *from, uint length,
        const CHARSET_INFO *cs)
{
    ASSERT_COLUMN_MARKED_FOR_WRITE;
    int delta;
    uchar bits = (uchar) (field_length & 7);

    for (; length && !*from; from++, length--) ;         // skip left 0's

    delta = bytes_in_rec - length;

    if (delta < 0 ||
            (delta == 0 && bits && (uint) (uchar) *from >= (uint) (1 << bits))) {
        memset(ptr, 0xff, bytes_in_rec);

        if (bits)
            *ptr &= ((1 << bits) - 1); /* set first uchar */

        if (table->in_use->really_abort_on_warning())
            set_warning(Sql_condition::WARN_LEVEL_WARN, ER_DATA_TOO_LONG, 1);
        else
            set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);

        return TYPE_WARN_OUT_OF_RANGE;
    }

    memset(ptr, 0, delta);
    memcpy(ptr + delta, from, length);
    return TYPE_OK;
}


void Field_bit_as_char::sql_type(String &res) const
{
    const CHARSET_INFO *cs = res.charset();
    ulong length = cs->cset->snprintf(cs, (char *) res.ptr(), res.alloced_length(),
                                      "bit(%d)", (int) field_length);
    res.length((uint) length);
}


/*****************************************************************************
  Handling of field and Create_field
*****************************************************************************/

/**
  Convert create_field::length from number of characters to number of bytes.
*/

void Create_field::create_length_to_internal_length(void)
{
    switch (sql_type) {
    case MYSQL_TYPE_TINY_BLOB:
    case MYSQL_TYPE_MEDIUM_BLOB:
    case MYSQL_TYPE_LONG_BLOB:
    case MYSQL_TYPE_BLOB:
    case MYSQL_TYPE_GEOMETRY:
    case MYSQL_TYPE_VAR_STRING:
    case MYSQL_TYPE_STRING:
    case MYSQL_TYPE_VARCHAR:
        length *= charset->mbmaxlen;
        key_length = length;
        pack_length = calc_pack_length(sql_type, length);
        break;

    case MYSQL_TYPE_ENUM:
    case MYSQL_TYPE_SET:
        /* Pack_length already calculated in sql_parse.cc */
        length *= charset->mbmaxlen;
        key_length = pack_length;
        break;

    case MYSQL_TYPE_BIT:
        if (f_bit_as_char(pack_flag))
            key_length = pack_length = ((length + 7) & ~7) / 8;

        else {
            pack_length = length / 8;
            /* We need one extra byte to store the bits we save among the null bits */
            key_length = pack_length + test(length & 7);
        }

        break;

    case MYSQL_TYPE_NEWDECIMAL:
        key_length = pack_length =
                         my_decimal_get_binary_size(my_decimal_length_to_precision(length,
                                 decimals,
                                 flags &
                                 UNSIGNED_FLAG),
                                 decimals);
        break;

    default:
        key_length = pack_length = calc_pack_length(sql_type, length);
        break;
    }
}


/**
  Init for a tmp table field. To be extended if need be.
*/
void Create_field::init_for_tmp_table(enum_field_types sql_type_arg,
                                      uint32 length_arg, uint32 decimals_arg,
                                      bool maybe_null, bool is_unsigned,
                                      uint pack_length_arg)
{
    DBUG_ENTER("Create_field::init_for_tmp_table");
    field_name = "";
    sql_type = sql_type_arg;
    char_length = length = length_arg;;
    unireg_check = Field::NONE;
    interval = 0;
    charset = &my_charset_bin;
    geom_type = Field::GEOM_GEOMETRY;
    DBUG_PRINT("enter", ("sql_type: %d, length: %u, pack_length: %u",
                         sql_type_arg, length_arg, pack_length_arg));

    /*
      These pack flags are crafted to get it correctly through the
      branches of make_field().
     */
    switch (sql_type_arg) {
    case MYSQL_TYPE_VARCHAR:
    case MYSQL_TYPE_VAR_STRING:
    case MYSQL_TYPE_STRING:
    case MYSQL_TYPE_SET:
        pack_flag = 0;
        break;

    case MYSQL_TYPE_GEOMETRY:
        pack_flag = FIELDFLAG_GEOM;
        break;

    case MYSQL_TYPE_ENUM:
        pack_flag = FIELDFLAG_INTERVAL;
        break;

    case MYSQL_TYPE_NEWDECIMAL:
        DBUG_ASSERT(decimals_arg <= DECIMAL_MAX_SCALE);

    case MYSQL_TYPE_DECIMAL:
    case MYSQL_TYPE_FLOAT:
    case MYSQL_TYPE_DOUBLE:
        pack_flag = FIELDFLAG_NUMBER |
                    (decimals_arg & FIELDFLAG_MAX_DEC) << FIELDFLAG_DEC_SHIFT;
        break;

    case MYSQL_TYPE_TINY_BLOB:
    case MYSQL_TYPE_MEDIUM_BLOB:
    case MYSQL_TYPE_LONG_BLOB:
    case MYSQL_TYPE_BLOB:
        pack_flag = FIELDFLAG_BLOB;
        break;

    case MYSQL_TYPE_BIT:
        pack_flag = FIELDFLAG_NUMBER | FIELDFLAG_TREAT_BIT_AS_CHAR;
        break;

    default:
        pack_flag = FIELDFLAG_NUMBER;
        break;
    }

    /*
      Set the pack flag correctly for the blob-like types. This sets the
      packtype to something that make_field can use. If the pack type is
      not set correctly, the packlength will be reeeeally wierd (like
      129 or so).
     */
    switch (sql_type_arg) {
    case MYSQL_TYPE_ENUM:
    case MYSQL_TYPE_SET:
    case MYSQL_TYPE_TINY_BLOB:
    case MYSQL_TYPE_MEDIUM_BLOB:
    case MYSQL_TYPE_LONG_BLOB:
    case MYSQL_TYPE_BLOB:
    case MYSQL_TYPE_GEOMETRY:
        // If you are going to use the above types, you have to pass a
        // pack_length as parameter. Assert that is really done.
        DBUG_ASSERT(pack_length_arg != ~0U);
        pack_flag |= pack_length_to_packflag(pack_length_arg);
        break;

    default:
        /* Nothing */
        break;
    }

    pack_flag |=
        (maybe_null ? FIELDFLAG_MAYBE_NULL : 0) |
        (is_unsigned ? 0 : FIELDFLAG_DECIMAL);
    DBUG_PRINT("debug", ("pack_flag: %s%s%s%s%s%s, pack_type: %d",
                         FLAGSTR(pack_flag, FIELDFLAG_BINARY),
                         FLAGSTR(pack_flag, FIELDFLAG_NUMBER),
                         FLAGSTR(pack_flag, FIELDFLAG_INTERVAL),
                         FLAGSTR(pack_flag, FIELDFLAG_GEOM),
                         FLAGSTR(pack_flag, FIELDFLAG_BLOB),
                         FLAGSTR(pack_flag, FIELDFLAG_DECIMAL),
                         f_packtype(pack_flag)));
    DBUG_VOID_RETURN;
}


/**
  Initialize a column definition object. Column definition objects can be used
  to construct Field objects.

  @param thd                   Session/Thread handle.
  @param fld_name              Column name.
  @param fld_type              Column type.
  @param fld_length            Column length.
  @param fld_decimals          Number of digits to the right of the decimal
                               point (if any.)
  @param fld_type_modifier     Additional type information.
  @param fld_default_value     Column default expression (if any.)
  @param fld_on_update_value   The expression in the ON UPDATE clause.
  @param fld_comment           Column comment.
  @param fld_change            Column change.
  @param fld_interval_list     Interval list (if any.)
  @param fld_charset           Column charset.
  @param fld_geom_type         Column geometry type (if any.)

  @retval
    FALSE on success.
  @retval
    TRUE  on error.
*/

bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
                        char *fld_length, char *fld_decimals,
                        uint fld_type_modifier, Item *fld_default_value,
                        Item *fld_on_update_value, LEX_STRING *fld_comment,
                        char *fld_change, List<String> *fld_interval_list,
                        const CHARSET_INFO *fld_charset, uint fld_geom_type)
{
    uint sign_len, allowed_type_modifier = 0;
    ulong max_field_charlength = MAX_FIELD_CHARLENGTH;
    DBUG_ENTER("Create_field::init()");
    field = 0;
    field_name = fld_name;
    flags = fld_type_modifier;
    charset = fld_charset;
    const bool on_update_is_function =
        (fld_on_update_value != NULL &&
         fld_on_update_value->type() == Item::FUNC_ITEM);

    if (fld_default_value != NULL && fld_default_value->type() == Item::FUNC_ITEM) {
        // We have a function default for insertions.
        def = NULL;
        unireg_check = on_update_is_function ?
                       Field::TIMESTAMP_DNUN_FIELD : // for insertions and for updates.
                       Field::TIMESTAMP_DN_FIELD;    // only for insertions.

    } else {
        // No function default for insertions. Either NULL or a constant.
        def = fld_default_value;

        if (on_update_is_function)
            // We have a function default for updates only.
            unireg_check = Field::TIMESTAMP_UN_FIELD;
        else
            // No function defaults.
            unireg_check = (fld_type_modifier & AUTO_INCREMENT_FLAG) != 0 ?
                           Field::NEXT_NUMBER : // Automatic increment.
                           Field::NONE;
    }

    decimals = fld_decimals ? (uint)atoi(fld_decimals) : 0;

    if (is_temporal_real_type(fld_type)) {
        flags |= BINARY_FLAG;

        //charset= &my_charset_numeric;
        if (decimals > DATETIME_MAX_DECIMALS) {
            my_error(ER_TOO_BIG_PRECISION, MYF(0),
                     decimals, fld_name, DATETIME_MAX_DECIMALS);
            DBUG_RETURN(TRUE);
        }

    } else if (decimals >= NOT_FIXED_DEC) {
        my_error(ER_TOO_BIG_SCALE, MYF(0), decimals, fld_name,
                 static_cast<ulong>(NOT_FIXED_DEC - 1));
        DBUG_RETURN(TRUE);
    }

    sql_type = fld_type;
    length = 0;
    change = fld_change;
    interval = 0;
    pack_length = key_length = 0;
    geom_type = (Field::geometry_type) fld_geom_type;
    interval_list.empty();
    comment = *fld_comment;

    /*
      Set NO_DEFAULT_VALUE_FLAG if this field doesn't have a default value and
      it is NOT NULL and not an AUTO_INCREMENT field.
    */
    if (!fld_default_value &&
            (fld_type_modifier & NOT_NULL_FLAG) &&
            !(fld_type_modifier & AUTO_INCREMENT_FLAG)) {
        /*
          TIMESTAMP columns get implicit DEFAULT value when
          explicit_defaults_for_timestamp is not set.
        */
        if (thd->variables.explicit_defaults_for_timestamp ||
                !is_timestamp_type(fld_type))
            flags |= NO_DEFAULT_VALUE_FLAG;
    }

    if (fld_length != NULL) {
        errno = 0;
        length = strtoul(fld_length, NULL, 10);

        if ((errno != 0) || (length > MAX_FIELD_BLOBLENGTH)) {
            my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), fld_name, MAX_FIELD_BLOBLENGTH);
            DBUG_RETURN(TRUE);
        }

        if (length == 0)
            fld_length = NULL; /* purecov: inspected */
    }

    sign_len = fld_type_modifier & UNSIGNED_FLAG ? 0 : 1;

    switch (fld_type) {
    case MYSQL_TYPE_TINY:
        if (!fld_length)
            length = MAX_TINYINT_WIDTH + sign_len;

        allowed_type_modifier = AUTO_INCREMENT_FLAG;
        break;

    case MYSQL_TYPE_SHORT:
        if (!fld_length)
            length = MAX_SMALLINT_WIDTH + sign_len;

        allowed_type_modifier = AUTO_INCREMENT_FLAG;
        break;

    case MYSQL_TYPE_INT24:
        if (!fld_length)
            length = MAX_MEDIUMINT_WIDTH + sign_len;

        allowed_type_modifier = AUTO_INCREMENT_FLAG;
        break;

    case MYSQL_TYPE_LONG:
        if (!fld_length)
            length = MAX_INT_WIDTH + sign_len;

        allowed_type_modifier = AUTO_INCREMENT_FLAG;
        break;

    case MYSQL_TYPE_LONGLONG:
        if (!fld_length)
            length = MAX_BIGINT_WIDTH;

        allowed_type_modifier = AUTO_INCREMENT_FLAG;
        break;

    case MYSQL_TYPE_NULL:
        break;

    case MYSQL_TYPE_NEWDECIMAL:
        my_decimal_trim(&length, &decimals);

        if (length > DECIMAL_MAX_PRECISION) {
            my_error(ER_TOO_BIG_PRECISION, MYF(0), static_cast<int>(length),
                     fld_name, static_cast<ulong>(DECIMAL_MAX_PRECISION));
            DBUG_RETURN(TRUE);
        }

        if (length < decimals) {
            my_error(ER_M_BIGGER_THAN_D, MYF(0), fld_name);
            DBUG_RETURN(TRUE);
        }

        length =
            my_decimal_precision_to_length(length, decimals,
                                           fld_type_modifier & UNSIGNED_FLAG);
        pack_length =
            my_decimal_get_binary_size(length, decimals);
        break;

    case MYSQL_TYPE_VARCHAR:
        /*
          Long VARCHAR's are automaticly converted to blobs in mysql_prepare_table
          if they don't have a default value
        */
        max_field_charlength = MAX_FIELD_VARCHARLENGTH;
        break;

    case MYSQL_TYPE_STRING:
        break;

    case MYSQL_TYPE_BLOB:
    case MYSQL_TYPE_TINY_BLOB:
    case MYSQL_TYPE_LONG_BLOB:
    case MYSQL_TYPE_MEDIUM_BLOB:
    case MYSQL_TYPE_GEOMETRY:
        if (fld_default_value) {
            /* Allow empty as default value. */
            String str, *res;
            res = fld_default_value->val_str(&str);
            /*
              A default other than '' is always an error, and any non-NULL
              specified default is an error in strict mode.
            */
            // if (res->length() || thd->is_strict_mode())
            // {
            //   my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0),
            //            fld_name); /* purecov: inspected */
            //   DBUG_RETURN(TRUE);
            // }
            // else
            // {
            //   /*
            //     Otherwise a default of '' is just a warning.
            //   */
            //   push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
            //                       ER_BLOB_CANT_HAVE_DEFAULT,
            //                       ER(ER_BLOB_CANT_HAVE_DEFAULT),
            //                       fld_name);
            // }
            // def= 0;
        }

        flags |= BLOB_FLAG;
        break;

    case MYSQL_TYPE_YEAR:
        if (!fld_length || length != 2)
            length = 4; /* Default length */

        flags |= ZEROFILL_FLAG | UNSIGNED_FLAG;
        break;

    case MYSQL_TYPE_FLOAT:
        /* change FLOAT(precision) to FLOAT or DOUBLE */
        allowed_type_modifier = AUTO_INCREMENT_FLAG;

        if (fld_length && !fld_decimals) {
            uint tmp_length = length;

            if (tmp_length > PRECISION_FOR_DOUBLE) {
                my_error(ER_WRONG_FIELD_SPEC, MYF(0), fld_name);
                DBUG_RETURN(TRUE);

            } else if (tmp_length > PRECISION_FOR_FLOAT) {
                sql_type = MYSQL_TYPE_DOUBLE;
                length = MAX_DOUBLE_STR_LENGTH;

            } else
                length = MAX_FLOAT_STR_LENGTH;

            decimals = NOT_FIXED_DEC;
            break;
        }

        if (!fld_length && !fld_decimals) {
            length =  MAX_FLOAT_STR_LENGTH;
            decimals = NOT_FIXED_DEC;
        }

        if (length < decimals &&
                decimals != NOT_FIXED_DEC) {
            my_error(ER_M_BIGGER_THAN_D, MYF(0), fld_name);
            DBUG_RETURN(TRUE);
        }

        break;

    case MYSQL_TYPE_DOUBLE:
        allowed_type_modifier = AUTO_INCREMENT_FLAG;

        if (!fld_length && !fld_decimals) {
            length = DBL_DIG + 7;
            decimals = NOT_FIXED_DEC;
        }

        if (length < decimals &&
                decimals != NOT_FIXED_DEC) {
            my_error(ER_M_BIGGER_THAN_D, MYF(0), fld_name);
            DBUG_RETURN(TRUE);
        }

        break;

    case MYSQL_TYPE_TIMESTAMP:
        /* Add flags for TIMESTAMP for 4.0 MYD and 4.0 InnoDB compatibility */
        flags |= ZEROFILL_FLAG | UNSIGNED_FLAG;

    /* Fall through */
    case MYSQL_TYPE_TIMESTAMP2:
        if (fld_length == NULL)
            length = MAX_DATETIME_WIDTH + (decimals ? (1 + decimals) : 0);

        else if (length != MAX_DATETIME_WIDTH) {
            /*
              We support only even TIMESTAMP lengths less or equal than 14
              and 19 as length of 4.1 compatible representation.  Silently
              shrink it to MAX_DATETIME_COMPRESSED_WIDTH.
            */
            DBUG_ASSERT(MAX_DATETIME_COMPRESSED_WIDTH < UINT_MAX);

            if (length != UINT_MAX)  /* avoid overflow; is safe because of min() */
                length = ((length + 1) / 2) * 2;

            length = min<ulong>(length, MAX_DATETIME_COMPRESSED_WIDTH);
        }

        /*
          Since we silently rewrite down to MAX_DATETIME_COMPRESSED_WIDTH bytes,
          the parser should not raise errors unless bizzarely large.
         */
        max_field_charlength = UINT_MAX;
        break;

    case MYSQL_TYPE_DATE:
        /* Old date type. */
        sql_type = MYSQL_TYPE_NEWDATE;

    /* fall trough */
    case MYSQL_TYPE_NEWDATE:
        length = MAX_DATE_WIDTH;
        break;

    case MYSQL_TYPE_TIME:
    case MYSQL_TYPE_TIME2:
        length = MAX_TIME_WIDTH + (decimals ? (1 + decimals) : 0);
        break;

    case MYSQL_TYPE_DATETIME:
    case MYSQL_TYPE_DATETIME2:
        length = MAX_DATETIME_WIDTH + (decimals ? (1 + decimals) : 0);
        break;

    case MYSQL_TYPE_SET: {
        pack_length = get_set_pack_length(fld_interval_list->elements);
        List_iterator<String> it(*fld_interval_list);
        String *tmp;

        while ((tmp = it++))
            interval_list.push_back(tmp);

        /*
          Set fake length to 1 to pass the below conditions.
          Real length will be set in mysql_prepare_table()
          when we know the character set of the column
        */
        length = 1;
        break;
    }

    case MYSQL_TYPE_ENUM: {
        /* Should be safe. */
        pack_length = get_enum_pack_length(fld_interval_list->elements);
        List_iterator<String> it(*fld_interval_list);
        String *tmp;

        while ((tmp = it++))
            interval_list.push_back(tmp);

        length = 1; /* See comment for MYSQL_TYPE_SET above. */
        break;
    }

    case MYSQL_TYPE_VAR_STRING:
        DBUG_ASSERT(0);  /* Impossible. */
        break;

    case MYSQL_TYPE_BIT: {
        if (!fld_length)
            length = 1;

        if (length > MAX_BIT_FIELD_LENGTH) {
            my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), fld_name,
                     static_cast<ulong>(MAX_BIT_FIELD_LENGTH));
            DBUG_RETURN(TRUE);
        }

        pack_length = (length + 7) / 8;
        break;
    }

    case MYSQL_TYPE_DECIMAL:
        DBUG_ASSERT(0); /* Was obsolete */
    }

    /* Remember the value of length */
    char_length = length;

    if (!(flags & BLOB_FLAG) &&
            ((length > max_field_charlength && fld_type != MYSQL_TYPE_SET &&
              fld_type != MYSQL_TYPE_ENUM &&
              (fld_type != MYSQL_TYPE_VARCHAR || fld_default_value)) ||
             ((length == 0) &&
              fld_type != MYSQL_TYPE_STRING &&
              fld_type != MYSQL_TYPE_VARCHAR && fld_type != MYSQL_TYPE_GEOMETRY))) {
        my_error((fld_type == MYSQL_TYPE_VAR_STRING ||
                  fld_type == MYSQL_TYPE_VARCHAR ||
                  fld_type == MYSQL_TYPE_STRING) ?  ER_TOO_BIG_FIELDLENGTH :
                 ER_TOO_BIG_DISPLAYWIDTH,
                 MYF(0),
                 fld_name, max_field_charlength); /* purecov: inspected */
        DBUG_RETURN(TRUE);
    }

    fld_type_modifier &= AUTO_INCREMENT_FLAG;

    if ((~allowed_type_modifier) & fld_type_modifier) {
        my_error(ER_WRONG_FIELD_SPEC, MYF(0), fld_name);
        DBUG_RETURN(TRUE);
    }

    DBUG_RETURN(FALSE); /* success */
}


enum_field_types get_blob_type_from_length(ulong length)
{
    enum_field_types type;

    if (length < 256)
        type = MYSQL_TYPE_TINY_BLOB;
    else if (length < 65536)
        type = MYSQL_TYPE_BLOB;
    else if (length < 256L * 256L * 256L)
        type = MYSQL_TYPE_MEDIUM_BLOB;
    else
        type = MYSQL_TYPE_LONG_BLOB;

    return type;
}


/*
  Make a field from the .frm file info
*/

uint32 calc_pack_length(enum_field_types type, uint32 length)
{
    switch (type) {
    case MYSQL_TYPE_VAR_STRING:
    case MYSQL_TYPE_STRING:
    case MYSQL_TYPE_SET:
    case MYSQL_TYPE_ENUM:
    case MYSQL_TYPE_NEWDECIMAL:
    case MYSQL_TYPE_DECIMAL:
        return (length);

    case MYSQL_TYPE_VARCHAR:
        return (length + (length < 256 ? 1 : 2));

    case MYSQL_TYPE_YEAR:
    case MYSQL_TYPE_TINY	:
        return 1;

    case MYSQL_TYPE_SHORT :
        return 2;

    case MYSQL_TYPE_INT24:
    case MYSQL_TYPE_NEWDATE:
        return 3;

    case MYSQL_TYPE_TIME:
        return 3;

    case MYSQL_TYPE_TIME2:
        return length > MAX_TIME_WIDTH ?
               my_time_binary_length(length - MAX_TIME_WIDTH - 1) : 3;

    case MYSQL_TYPE_TIMESTAMP:
        return 4;

    case MYSQL_TYPE_TIMESTAMP2:
        return length > MAX_DATETIME_WIDTH ?
               my_timestamp_binary_length(length - MAX_DATETIME_WIDTH - 1) : 4;

    case MYSQL_TYPE_DATE:
    case MYSQL_TYPE_LONG	:
        return 4;

    case MYSQL_TYPE_FLOAT :
        return sizeof(float);

    case MYSQL_TYPE_DOUBLE:
        return sizeof(double);

    case MYSQL_TYPE_DATETIME:
        return 8;

    case MYSQL_TYPE_DATETIME2:
        return length > MAX_DATETIME_WIDTH ?
               my_datetime_binary_length(length - MAX_DATETIME_WIDTH - 1) : 5;

    case MYSQL_TYPE_LONGLONG:
        return 8;	/* Don't crash if no longlong */

    case MYSQL_TYPE_NULL	:
        return 0;

    case MYSQL_TYPE_TINY_BLOB:
        return 1 + portable_sizeof_char_ptr;

    case MYSQL_TYPE_BLOB:
        return 2 + portable_sizeof_char_ptr;

    case MYSQL_TYPE_MEDIUM_BLOB:
        return 3 + portable_sizeof_char_ptr;

    case MYSQL_TYPE_LONG_BLOB:
        return 4 + portable_sizeof_char_ptr;

    case MYSQL_TYPE_GEOMETRY:
        return 4 + portable_sizeof_char_ptr;

    case MYSQL_TYPE_BIT:
        return length / 8;

    default:
        return 0;
    }
}


uint pack_length_to_packflag(uint type)
{
    switch (type) {
    case 1:
        return f_settype((uint) MYSQL_TYPE_TINY);

    case 2:
        return f_settype((uint) MYSQL_TYPE_SHORT);

    case 3:
        return f_settype((uint) MYSQL_TYPE_INT24);

    case 4:
        return f_settype((uint) MYSQL_TYPE_LONG);

    case 8:
        return f_settype((uint) MYSQL_TYPE_LONGLONG);
    }

    return 0;					// This shouldn't happen
}


Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length,
                  uchar *null_pos, uchar null_bit,
                  uint pack_flag,
                  enum_field_types field_type,
                  const CHARSET_INFO *field_charset,
                  Field::geometry_type geom_type,
                  Field::utype unireg_check,
                  TYPELIB *interval,
                  const char *field_name,
                  MEM_ROOT *mem_root /* default= NULL */)
{
    uchar *UNINIT_VAR(bit_ptr);
    uchar UNINIT_VAR(bit_offset);

    if (field_type == MYSQL_TYPE_BIT && !f_bit_as_char(pack_flag)) {
        bit_ptr = null_pos;
        bit_offset = null_bit;

        if (f_maybe_null(pack_flag)) {       // if null field
            bit_ptr += (null_bit == 7);       // shift bit_ptr and bit_offset
            bit_offset = (bit_offset + 1) & 7;
        }
    }

    if (!f_maybe_null(pack_flag)) {
        null_pos = 0;
        null_bit = 0;

    } else
        null_bit = ((uchar) 1) << null_bit;

    if (is_temporal_real_type(field_type))
        field_charset = &my_charset_numeric;

    DBUG_PRINT("debug", ("field_type: %d, field_length: %u, interval: %p, pack_flag: %s%s%s%s%s",
                         field_type, field_length, interval,
                         FLAGSTR(pack_flag, FIELDFLAG_BINARY),
                         FLAGSTR(pack_flag, FIELDFLAG_INTERVAL),
                         FLAGSTR(pack_flag, FIELDFLAG_NUMBER),
                         FLAGSTR(pack_flag, FIELDFLAG_PACK),
                         FLAGSTR(pack_flag, FIELDFLAG_BLOB)));

    if (mem_root == NULL)
        mem_root = current_thd->mem_root;

    if (f_is_alpha(pack_flag)) {
        if (!f_is_packed(pack_flag)) {
            if (field_type == MYSQL_TYPE_STRING ||
                    field_type == MYSQL_TYPE_DECIMAL ||   // 3.23 or 4.0 string
                    field_type == MYSQL_TYPE_VAR_STRING)
                return new (mem_root)Field_string(ptr, field_length, null_pos, null_bit,
                                                  unireg_check, field_name,
                                                  field_charset);

            if (field_type == MYSQL_TYPE_VARCHAR)
                return new (mem_root)Field_varstring(ptr, field_length,
                                                     HA_VARCHAR_PACKLENGTH(field_length),
                                                     null_pos, null_bit,
                                                     unireg_check, field_name,
                                                     share,
                                                     field_charset);

            return 0;                                 // Error
        }

        uint pack_length = calc_pack_length((enum_field_types)
                                            f_packtype(pack_flag),
                                            field_length);
#ifdef HAVE_SPATIAL

        if (f_is_geom(pack_flag))
            return new(mem_root) Field_geom(ptr, null_pos, null_bit,
                                            unireg_check, field_name, share,
                                            pack_length, geom_type);

#endif

        if (f_is_blob(pack_flag))
            return new(mem_root) Field_blob(ptr, null_pos, null_bit,
                                            unireg_check, field_name, share,
                                            pack_length, field_charset);

        if (interval) {
            if (f_is_enum(pack_flag))
                return new (mem_root)Field_enum(ptr, field_length, null_pos, null_bit,
                                                unireg_check, field_name,
                                                pack_length, interval, field_charset);
            else
                return new (mem_root)Field_set(ptr, field_length, null_pos, null_bit,
                                               unireg_check, field_name,
                                               pack_length, interval, field_charset);
        }
    }

    switch (field_type) {
    case MYSQL_TYPE_DECIMAL:
        return new (mem_root)Field_decimal(ptr, field_length, null_pos, null_bit,
                                           unireg_check, field_name,
                                           f_decimals(pack_flag),
                                           f_is_zerofill(pack_flag) != 0,
                                           f_is_dec(pack_flag) == 0);

    case MYSQL_TYPE_NEWDECIMAL:
        return new (mem_root)Field_new_decimal(ptr, field_length, null_pos, null_bit,
                                               unireg_check, field_name,
                                               f_decimals(pack_flag),
                                               f_is_zerofill(pack_flag) != 0,
                                               f_is_dec(pack_flag) == 0);

    case MYSQL_TYPE_FLOAT:
        return new (mem_root)Field_float(ptr, field_length, null_pos, null_bit,
                                         unireg_check, field_name,
                                         f_decimals(pack_flag),
                                         f_is_zerofill(pack_flag) != 0,
                                         f_is_dec(pack_flag) == 0);

    case MYSQL_TYPE_DOUBLE:
        return new (mem_root)Field_double(ptr, field_length, null_pos, null_bit,
                                          unireg_check, field_name,
                                          f_decimals(pack_flag),
                                          f_is_zerofill(pack_flag) != 0,
                                          f_is_dec(pack_flag) == 0);

    case MYSQL_TYPE_TINY:
        return new (mem_root)Field_tiny(ptr, field_length, null_pos, null_bit,
                                        unireg_check, field_name,
                                        f_is_zerofill(pack_flag) != 0,
                                        f_is_dec(pack_flag) == 0);

    case MYSQL_TYPE_SHORT:
        return new (mem_root)Field_short(ptr, field_length, null_pos, null_bit,
                                         unireg_check, field_name,
                                         f_is_zerofill(pack_flag) != 0,
                                         f_is_dec(pack_flag) == 0);

    case MYSQL_TYPE_INT24:
        return new (mem_root)Field_medium(ptr, field_length, null_pos, null_bit,
                                          unireg_check, field_name,
                                          f_is_zerofill(pack_flag) != 0,
                                          f_is_dec(pack_flag) == 0);

    case MYSQL_TYPE_LONG:
        return new (mem_root)Field_long(ptr, field_length, null_pos, null_bit,
                                        unireg_check, field_name,
                                        f_is_zerofill(pack_flag) != 0,
                                        f_is_dec(pack_flag) == 0);

    case MYSQL_TYPE_LONGLONG:
        return new (mem_root)Field_longlong(ptr, field_length, null_pos, null_bit,
                                            unireg_check, field_name,
                                            f_is_zerofill(pack_flag) != 0,
                                            f_is_dec(pack_flag) == 0);

    case MYSQL_TYPE_TIMESTAMP:
        return new (mem_root)Field_timestamp(ptr, field_length, null_pos, null_bit,
                                             unireg_check, field_name);

    case MYSQL_TYPE_TIMESTAMP2:
        return new (mem_root)Field_timestampf(ptr, null_pos, null_bit,
                                              unireg_check, field_name,
                                              field_length > MAX_DATETIME_WIDTH ?
                                              field_length - 1 - MAX_DATETIME_WIDTH : 0);

    case MYSQL_TYPE_YEAR:
        return new (mem_root)Field_year(ptr, field_length, null_pos, null_bit,
                                        unireg_check, field_name);

    case MYSQL_TYPE_NEWDATE:
    case MYSQL_TYPE_DATE:
        return new (mem_root)Field_newdate(ptr, null_pos, null_bit, unireg_check, field_name);

    case MYSQL_TYPE_TIME:
        return new (mem_root)Field_time(ptr, null_pos, null_bit,
                                        unireg_check, field_name);

    case MYSQL_TYPE_TIME2:
        return new (mem_root)Field_timef(ptr, null_pos, null_bit,
                                         unireg_check, field_name,
                                         (field_length > MAX_TIME_WIDTH) ?
                                         field_length - 1 - MAX_TIME_WIDTH : 0);

    case MYSQL_TYPE_DATETIME:
        return new (mem_root)Field_datetime(ptr, null_pos, null_bit,
                                            unireg_check, field_name);

    case MYSQL_TYPE_DATETIME2:
        return new (mem_root)Field_datetimef(ptr, null_pos, null_bit,
                                             unireg_check, field_name,
                                             (field_length > MAX_DATETIME_WIDTH) ?
                                             field_length - 1 - MAX_DATETIME_WIDTH : 0);

    case MYSQL_TYPE_NULL:
        return new (mem_root)Field_null(ptr, field_length, unireg_check, field_name,
                                        field_charset);

    case MYSQL_TYPE_BIT:
        return f_bit_as_char(pack_flag) ?
               new (mem_root)Field_bit_as_char(ptr, field_length, null_pos, null_bit,
                                               unireg_check, field_name) :
               new (mem_root)Field_bit(ptr, field_length, null_pos, null_bit, bit_ptr,
                                       bit_offset, unireg_check, field_name);

    default:					// Impossible (Wrong version)
        break;
    }

    return 0;
}


/**
    Constructs a column definition from an actual column object. This is a
    reverse-engineering procedure that creates a column definition object as
    produced by the parser (Create_field) from a resolved column object
    (Field).

    @param old_field  The column object from which the column definition is
                      constructed.
    @param orig_field Used for copying default values. This parameter may be
                      NULL, but if present it is used for copying default
                      values.

    Default values are copied into an Item_string unless:
    @li The default value is a function.
    @li There is no default value.
    @li old_field is a BLOB column.
    @li old_field has its data pointer improperly initialized.
*/

Create_field::Create_field(Field *old_field, Field *orig_field) :
    field_name(old_field->field_name),
    change(old_field->field_name),
    comment(old_field->comment),
    sql_type(old_field->real_type()),
    length(old_field->field_length),
    decimals(old_field->decimals()),
    flags(old_field->flags),
    pack_length(old_field->pack_length()),
    key_length(old_field->key_length()),
    unireg_check(old_field->unireg_check),
    charset(old_field->charset()),		// May be NULL ptr
    field(old_field)
{
    switch (sql_type) {
    case MYSQL_TYPE_BLOB:
        switch (pack_length - portable_sizeof_char_ptr) {
        case  1:
            sql_type = MYSQL_TYPE_TINY_BLOB;
            break;

        case  2:
            sql_type = MYSQL_TYPE_BLOB;
            break;

        case  3:
            sql_type = MYSQL_TYPE_MEDIUM_BLOB;
            break;

        default:
            sql_type = MYSQL_TYPE_LONG_BLOB;
            break;
        }

        length /= charset->mbmaxlen;
        key_length /= charset->mbmaxlen;
        break;

    case MYSQL_TYPE_STRING:

        /* Change CHAR -> VARCHAR if dynamic record length */
        if (old_field->type() == MYSQL_TYPE_VAR_STRING)
            sql_type = MYSQL_TYPE_VARCHAR;

    /* fall through */

    case MYSQL_TYPE_ENUM:
    case MYSQL_TYPE_SET:
    case MYSQL_TYPE_VARCHAR:
    case MYSQL_TYPE_VAR_STRING:
        /* This is corrected in create_length_to_internal_length */
        length = (length + charset->mbmaxlen - 1) / charset->mbmaxlen;
        break;
#ifdef HAVE_SPATIAL

    case MYSQL_TYPE_GEOMETRY:
        geom_type = ((Field_geom *)old_field)->geom_type;
        break;
#endif

    case MYSQL_TYPE_YEAR:
        if (length != 4) {
            push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
                                ER_INVALID_YEAR_COLUMN_LENGTH,
                                ER(ER_INVALID_YEAR_COLUMN_LENGTH),
                                length);
            length = 4; // convert obsolete YEAR(2) to YEAR(4)
        }

        break;

    default:
        break;
    }

    if (flags & (ENUM_FLAG | SET_FLAG))
        interval = ((Field_enum *) old_field)->typelib;
    else
        interval = 0;

    def = 0;
    char_length = length;

    /*
      Copy the default value from the column object orig_field, if supplied. We
      do this if all these conditions are met:

      - The column has a constant default value.

      - The column type is not a BLOB type.

      - The original column (old_field) was properly initialized with a record
        buffer pointer.
    */
    if (!(flags & (NO_DEFAULT_VALUE_FLAG | BLOB_FLAG)) &&
            old_field->ptr != NULL &&
            orig_field != NULL &&
            (!real_type_with_now_as_default(sql_type) ||
             !old_field->has_insert_default_function())) {
        char buff[MAX_FIELD_WIDTH];
        String tmp(buff, sizeof(buff), charset);
        my_ptrdiff_t diff;
        /* Get the value from default_values */
        diff = (my_ptrdiff_t) (orig_field->table->s->default_values -
                               orig_field->table->record[0]);
        orig_field->move_field_offset(diff);	// Points now at default_values

        if (!orig_field->is_real_null()) {
            char buff[MAX_FIELD_WIDTH], *pos;
            String tmp(buff, sizeof(buff), charset), *res;
            res = orig_field->val_str(&tmp);
            pos = (char *) sql_strmake(res->ptr(), res->length());
            def = new Item_string(pos, res->length(), charset);
        }

        orig_field->move_field_offset(-diff);	// Back to record[0]
    }
}


/**
  maximum possible character length for blob.

  This method is used in Item_field::set_field to calculate
  max_length for Item.

  For example:
    CREATE TABLE t2 SELECT CONCAT(tinyblob_utf8_column) FROM t1;
  must create a "VARCHAR(255) CHARACTER SET utf8" column.

  @return
    length
*/

uint32 Field_blob::char_length()
{
    switch (packlength) {
    case 1:
        return 255;

    case 2:
        return 65535;

    case 3:
        return 16777215;

    case 4:
        return (uint32) 4294967295U;

    default:
        DBUG_ASSERT(0); // we should never go here
        return 0;
    }
}


/**
  maximum possible display length for blob.

  @return
    length
*/

uint32 Field_blob::max_display_length()
{
    switch (packlength) {
    case 1:
        return 255 * field_charset->mbmaxlen;

    case 2:
        return 65535 * field_charset->mbmaxlen;

    case 3:
        return 16777215 * field_charset->mbmaxlen;

    case 4:
        return (uint32) 4294967295U;

    default:
        DBUG_ASSERT(0); // we should never go here
        return 0;
    }
}


/*****************************************************************************
 Warning handling
*****************************************************************************/

/**
  Produce warning or note about data saved into field.

  @param level            - level of message (Note/Warning/Error)
  @param code             - error code of message to be produced
  @param cut_increment    - whenever we should increase cut fields count

  @note
    This function won't produce warning and increase cut fields counter
    if count_cuted_fields == CHECK_FIELD_IGNORE for current thread.

    if count_cuted_fields == CHECK_FIELD_IGNORE then we ignore notes.
    This allows us to avoid notes in optimisation, like convert_constant_item().

  @retval
    1 if count_cuted_fields == CHECK_FIELD_IGNORE and error level is not NOTE
  @retval
    0 otherwise
*/

bool Field::set_warning(Sql_condition::enum_warning_level level, uint code,
                        int cut_increment) const
{
    /*
      If this field was created only for type conversion purposes it
      will have table == NULL.
    */
    THD *thd = table ? table->in_use : current_thd;

    if (thd->count_cuted_fields) {
        thd->cuted_fields += cut_increment;
        push_warning_printf(thd, level, code, ER(code), field_name,
                            thd->get_stmt_da()->current_row_for_warning());
        return 0;
    }

    return level >= Sql_condition::WARN_LEVEL_WARN;
}


/**
  Produce warning or note about double datetime data saved into field.

  @param level            level of message (Note/Warning/Error)
  @param code             error code of message to be produced
  @param val              error parameter (the value)
  @param ts_type          type of datetime value (datetime/date/time)
  @param cut_increment    whenever we should increase cut fields count
  @note
    This function will always produce some warning but won't increase cut
    fields counter if count_cuted_fields == FIELD_CHECK_IGNORE for current
    thread.
*/
void Field_temporal::set_datetime_warning(Sql_condition::enum_warning_level level,
        uint code,
        ErrConvString val,
        timestamp_type ts_type,
        int cut_increment)
{
    THD *thd = table ? table->in_use : current_thd;

    if (thd->really_abort_on_warning() ||
            set_warning(level, code, cut_increment))
        make_truncated_value_warning(thd, level, val, ts_type, field_name);
}
